<?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: Daniel Rotter</title>
    <description>The latest articles on DEV Community by Daniel Rotter (@danrot90).</description>
    <link>https://dev.to/danrot90</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%2F357893%2Fc7493783-13f7-4b91-a82c-47b383ac0ead.jpg</url>
      <title>DEV Community: Daniel Rotter</title>
      <link>https://dev.to/danrot90</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/danrot90"/>
    <language>en</language>
    <item>
      <title>Don't be shy, start your own meetup!</title>
      <dc:creator>Daniel Rotter</dc:creator>
      <pubDate>Sat, 08 Nov 2025 13:40:51 +0000</pubDate>
      <link>https://dev.to/danrot90/dont-be-shy-start-your-own-meetup-2kn</link>
      <guid>https://dev.to/danrot90/dont-be-shy-start-your-own-meetup-2kn</guid>
      <description>&lt;p&gt;Some time ago I stumbled upon &lt;a href="https://fosstodon.org/@JoshuaKGoldberg/114714095484577818" rel="noopener noreferrer"&gt;this mastodon post by Josh Goldberg about starting a meetup&lt;/a&gt;, which – as a meetup founder and organizer myself – I found quite interesting. But the reason for that was not that I recognized so many similarities, actually for the exact opposite reason: &lt;strong&gt;My experience with starting and continuing to run our &lt;a href="https://www.meetup.com/de-DE/vlbgwebdev/" rel="noopener noreferrer"&gt;VlbgWebDev meetup&lt;/a&gt; was completely different, mostly because I felt it is much less work than Josh made it sound.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I think the main reason for these different paths is the area in which these meetups take place. Josh's &lt;a href="https://bostonts.club/" rel="noopener noreferrer"&gt;Boston TS Club&lt;/a&gt; takes place in &lt;a href="https://en.wikipedia.org/wiki/Boston" rel="noopener noreferrer"&gt;Boston&lt;/a&gt; with a population of almost 5 million in its metropolitan area. My guess is that all kind of (developer) meetups were already existing and running for quite a time, given their huge number of potential organizers and attendees. In comparison, the "Vlbg" part in our meetup stands for &lt;a href="https://en.wikipedia.org/wiki/Vorarlberg" rel="noopener noreferrer"&gt;Vorarlberg&lt;/a&gt;, which is the westernmost state of Austria. It only has about 400 thousand inhabitants with &lt;a href="https://en.wikipedia.org/wiki/Dornbirn" rel="noopener noreferrer"&gt;Dornbirn&lt;/a&gt; being the biggest city, which recently has reached a population of 50 thousand.&lt;/p&gt;

&lt;p&gt;When I saw Josh's post I immediately described my experience starting a meetup, but I had the feeling that this would warrant its own blog post. So in the next few lines I like to described that in more detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  The inspiration
&lt;/h2&gt;

&lt;p&gt;Back in 2013 I spent my exchange semester in Copenhagen, Denmark. During that time I attended &lt;a href="https://www.meetup.com/copenhagenjs/" rel="noopener noreferrer"&gt;CopenhagenJS&lt;/a&gt; a few times. I really liked the experience and thought that it was a pity that nothing like that existed back at home. I have been told that there was a &lt;a href="https://www.facebook.com/groups/135326164620/" rel="noopener noreferrer"&gt;regulars' table called "Online Stammtisch Vorarlberg"&lt;/a&gt;, but it was not very active at the time anymore and the format was also very different from e.g. CopenhagenJS.&lt;/p&gt;

&lt;p&gt;After returning from my exchange semester I started working at &lt;a href="https://www.massiveart.com/" rel="noopener noreferrer"&gt;MASSIVE ART&lt;/a&gt; and told my colleagues about CopenhagenJS. Especially &lt;a href="https://www.linkedin.com/in/thomasschedler/" rel="noopener noreferrer"&gt;Thomas Schedler&lt;/a&gt; motivated me to start a meetup in Vorarlberg and our employer MASSIVE ART promised to support us by providing a venue for the meetups.&lt;/p&gt;

&lt;h2&gt;
  
  
  The topic
&lt;/h2&gt;

&lt;p&gt;The number one question was the topic of the meetup. We definitely wanted to gather the web development community from the area, so it had to be something web development related. At MASSIVE ART we mainly used PHP and JavaScript, but from what we knew from other companies in the area we were afraid that doing a PHP or JavaScript meetup would attract too few developers to reach a critical mass. We then settled for web development as a topic, since we felt that there might be enough people being interested in that topic and interested people would be willing to learn a bit about the concept of languages they are not using in there everyday's work. I still think that this is the sweet spot between being too narrow (e.g. focusing a language) and too broad (e.g. just a development meetup). Until today we use that topic, but we try to use it as liberally as possible, i.e. any topic that a web developer might be in touch with is allowed, which includes web technologies like HTML, CSS and JavaScript directly, but also databases, server-side programming using any language, logging, APIs, and nowadays also artificial intelligence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I think a relatively broad topic like ours only works in not so urban areas, since – even though it goes against their cliché – developers like to talk to other fellow developers. However, if there are more specific meetups meeting their interests better, they are likely going to visit those instead of the very generic one.&lt;/strong&gt; And the likelyhood for that increases with the number of people living in the area.&lt;/p&gt;

&lt;h2&gt;
  
  
  The format
&lt;/h2&gt;

&lt;p&gt;After deciding on the topic we had to define the format of a meetup. We drew a lot of inspiration from CopenhagenJS and set it up like the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The meetup takes place on the second tuesday of each month&lt;/li&gt;
&lt;li&gt;The venue is provided by a host that changes from month to month (with MASSIVE ART being the backup)&lt;/li&gt;
&lt;li&gt;The drinks are also provided by the host&lt;/li&gt;
&lt;li&gt;There will be two talks (lasting for someting between 15 and 60 minutes) for each meetup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some of this things changed a bit over time (e.g. the meetup is happening on the third wednesday now and hosts also started to provide food), but mostly this is how it works for us until today. Since some time ago we also have a summer break (July and August), because usually these are not the best attended meetups, which should not be a big surprise given that's the holiday season. For similar reasons we started not doing a "real" meetup in December, instead we go to the local Christmas market.&lt;/p&gt;

&lt;h2&gt;
  
  
  The first meetup
&lt;/h2&gt;

&lt;p&gt;I guess this is the most tricky part, given that you usually do not have any visibility at that point (unless you are a well-known figure in the industry). In order to match our format we needed two speakers, so that was our first goal. Luckily, &lt;a href="https://www.linkedin.com/in/erfan-ebrahimnia/" rel="noopener noreferrer"&gt;Erfan Ebrahimnia&lt;/a&gt; was working at MASSIVE ART at the time, and volunteered to have a talk about &lt;a href="https://gruntjs.com/" rel="noopener noreferrer"&gt;Grunt&lt;/a&gt;. Additionally, we have tweeted (yes, I did that at the time) to find another speaker, to which &lt;a href="https://www.linkedin.com/in/cklocker/" rel="noopener noreferrer"&gt;Christoph Klocker&lt;/a&gt; responded to suggest doing a talk about &lt;a href="https://sass-lang.com/" rel="noopener noreferrer"&gt;Sass&lt;/a&gt;. And yes, both of these topics were cutting edge back then!&lt;/p&gt;

&lt;p&gt;After finding some speakers we were able to set a date: the 10th of December 2013. A few weeks in advance I started doing a few Google searches to find some companies doing web development in the area and sent emails consisting of just a few lines explaining what the goal of our meetup was (mainly learning and networking) and some event information (where, when, and the talks). The email also included a link to our &lt;a href="https://en.wikipedia.org/wiki/Lanyrd" rel="noopener noreferrer"&gt;Lanyrd&lt;/a&gt; page, on which interested people can register for the event. Since most email addresses I found on the world wide web were some generic company contact addresses, I also asked to forward that email to their developers.&lt;/p&gt;

&lt;p&gt;To my surprise 25 developers showed up, which is a very good number for doing something out of the blue! The talks were nice and many people also stayed for the networking afterwards. In my opinion the first meetup was a real success and motivated me to continue running the meetup. &lt;strong&gt;The most important trait for a meetup organizer after the first successful (or even not so successful) meetup is persistence.&lt;/strong&gt; Having a fixed date like the second tuesday of every month really helps with that. If you are being successful the pool of people knowing about the meetup will definitely grow, because attendees will tell their friends and co-workers about it.&lt;/p&gt;

&lt;p&gt;The rest of this article will explain how we kept the meetup going.&lt;/p&gt;

&lt;h2&gt;
  
  
  The organizers
&lt;/h2&gt;

&lt;p&gt;Currently I am organizing the meetup with &lt;a href="https://www.linkedin.com/in/philip-heimboeck/" rel="noopener noreferrer"&gt;Philip Heimböck&lt;/a&gt;, in the past &lt;a href="https://www.linkedin.com/in/johannesmoser/" rel="noopener noreferrer"&gt;Johannes Moser&lt;/a&gt;, and the previously mentioned &lt;a href="https://www.linkedin.com/in/thomasschedler/" rel="noopener noreferrer"&gt;Thomas Schedler&lt;/a&gt; were also co-organizers.&lt;/p&gt;

&lt;p&gt;It would be nice to have a bigger team, but all attempts to achieve that have not lead anywhere until now. I would say even doing it alone would be worth it, although the community might not survive if that single person gives up. But even if that happens it is better to have had a meetup than it would never have happened. And who knows, maybe you get to know some regular meetup visitor who steps up helping with the organization.&lt;/p&gt;

&lt;p&gt;I have heard about other meetup organizers doing a lot, starting from fund raising, printing merchandise, recording videos, organize catering, etc. All we do is finding hosts and speakers (more on that later), add the monthly event to &lt;a href="https://www.meetup.com" rel="noopener noreferrer"&gt;meetup.com&lt;/a&gt;, and announce the meetup on our &lt;a href="https://www.linkedin.com/company/vlbgwebdev/" rel="noopener noreferrer"&gt;LinkedIn group&lt;/a&gt; and a Slack channel. I don't track time, but it really does not feel like a lot, my guess would be that I am spending less than 4 hours a month organizing. So if you are afraid that organizing a meetup is a lot of work, trust me if I tell you it does not have to be. Maybe the meetup would be more successful if we put more effort into this, but since we are not getting paid for that the way it is currently going totally works for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  The audience
&lt;/h2&gt;

&lt;p&gt;Our audience varies stark. We've had all kind of numbers starting from 3 up to 70 attendees for a single meetup, and I would say something between 20 and 30 attendees is the average. We have fairly regular visitors and some people only showing up if they are interested in one of the talks. The biggest factors regarding the size of the audience are most likely the topic of the talks (I guess you know which topics are currently hyped) and the venue (how many of the employees stay for the meetup and usually more people also show up out of curiousity if a company moved into a new office).&lt;/p&gt;

&lt;p&gt;Having people attending your meetup is kind of the reward for organizing it, but it still should not be overvalued. Of course, if every meetup only has a low one-digit number of attendees I would probably lose the motiviation to organize it, but at the same time I do not need to have over 50 people at every meetup to keep going.&lt;/p&gt;

&lt;p&gt;Something interesting we have observed is that after the pandemic (or at least after the lockdowns) the number of attendees increased and stayed on a high level. I guess people are tired of sitting in video calls all day, and are happy to make some real connections at a meetup, even if it is not within business hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  The speakers
&lt;/h2&gt;

&lt;p&gt;Getting speakers is probably one of the hardest parts. There are not loads of people who like to talk in public, and sometimes it feels to me like there are even less of them among developers. Especially at the beginning we often had the same few speakers, and sometime I also had to chip in myself. I have also heard similar things from organizers of other meetup groups.&lt;/p&gt;

&lt;p&gt;However, the situation improved quite a bit for a while, leading to a potential speaker having to wait for over an entire year from suggesting a topic to me to actually presenting it. The cause for this was two-fold: First, there were just many people suggesting topics at the same time coincidentally. But the second cause constantly helps us with finding speakers: Companies realized that if they only host and do not provide a talk, it looks to the audience like they do not have any interesting work, making them less attractive in the eyes of potential new hires (because at least on reason companies do this to improve their reputation and in turn have an easier time to find new employees). So it became way more accustom that the company hosting provides at least one and sometimes even two talks. They still run the topics by the organization team, which ensures that the topics are interesting and at least helps avoiding having talks which are only about presenting the company.&lt;/p&gt;

&lt;p&gt;At the beginning we sometimes only had one talk, because we did not find enough speakers. Then it got a bit better, and later we sometimes had no talk for a meetup at all. Then we started to think about using alternative ways of providing some learnings. Some things we did when we had no talk at hand were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Code_golf" rel="noopener noreferrer"&gt;Code golf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Kata#Outside_martial_arts" rel="noopener noreferrer"&gt;Code katas&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Public code reviews&lt;/li&gt;
&lt;li&gt;Watch a recorded conference talk&lt;/li&gt;
&lt;li&gt;Group discussions&lt;/li&gt;
&lt;li&gt;Attendees showing their setup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The code golf meetup (which is about solving a problem with as little source code as possible) was not the best visited one, but it certainly was one of the more fun ones. People were really eager in presenting their solutions and had a good time trying out stuff you probably would not do when writing an application you have to maintain for a longer time.&lt;/p&gt;

&lt;p&gt;Once we also had a public code review, where a solo developer showed what they have implemented and actively asked for feedback, which I enjoyed as well. The "Show your setup" sessions also were interesting, since people seem to have an easier time to just quickly present a small interesting tool.&lt;/p&gt;

&lt;p&gt;The others did not work that well for us, but I guess if you have no speakers they would also be okay. I think it is still better to have a meetup with such alternative sessions than skipping the meetup altogether (see my point about persistence above).&lt;/p&gt;

&lt;h2&gt;
  
  
  The hosts
&lt;/h2&gt;

&lt;p&gt;As mentioned in the section about the first meetup, the easiest way to get started is to host at your employer's office (assuming you are employed). We did not ask for much from the hosts at the beginning, it was only allowing people to be in the office, have some screen or projector available, and provide some drinks. In my case MASSIVE ART was kind enough to host the first five meetups.&lt;/p&gt;

&lt;p&gt;After some time attendees started offering to suggest hosting at their office (or their employer's office), so slowly we expanded the number of hosts. Some of these companies are my go-tos nowadays, in case nothing comes up by itself, and (I think) they happily host the meetup whenever I ask them. I just want to give them a quick shout out starting with the companies hosting the meetup most often:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.massiveart.com/" rel="noopener noreferrer"&gt;MASSIVE ART&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.fusonic.net/" rel="noopener noreferrer"&gt;Fusonic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.webgears-group.com/" rel="noopener noreferrer"&gt;Webgears&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.towa-digital.com" rel="noopener noreferrer"&gt;ToWa&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.russmedia.com/" rel="noopener noreferrer"&gt;Russmedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cratedb.com/" rel="noopener noreferrer"&gt;CRATE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.prisma-zentrum.com/initiativen/coworking/campus-v-coworking" rel="noopener noreferrer"&gt;CAMPUS Dornbirn Coworking&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.valantic.com" rel="noopener noreferrer"&gt;valantic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.antiloop.com/" rel="noopener noreferrer"&gt;Antiloop&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.yummy.digital/" rel="noopener noreferrer"&gt;Yummy Publishing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.starsmedia.com/" rel="noopener noreferrer"&gt;Starsmedia.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.posedio.com/" rel="noopener noreferrer"&gt;Posedio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fortix.io/" rel="noopener noreferrer"&gt;FORTIX&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://digitaleinitiativen.at/" rel="noopener noreferrer"&gt;Digitale Initiativen&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.arkulpa.at/" rel="noopener noreferrer"&gt;Arkulpa&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://muut.at/" rel="noopener noreferrer"&gt;Muut Offices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.vkw.at/" rel="noopener noreferrer"&gt;VKW&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tree.ly/" rel="noopener noreferrer"&gt;Tree.ly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.dornbirn.at/" rel="noopener noreferrer"&gt;Stadt Dornbirn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.perfany.at/" rel="noopener noreferrer"&gt;Perfany&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.lovelysystems.com/" rel="noopener noreferrer"&gt;Lovely Systems&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://limifyze.com" rel="noopener noreferrer"&gt;Limyfize&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.honoluluhotel.at/" rel="noopener noreferrer"&gt;Honolulu Hotel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.glesshub.at/" rel="noopener noreferrer"&gt;GlessHub Coworking&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.doppelmayr.com/" rel="noopener noreferrer"&gt;Doppelmayr&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://digit-one.com/" rel="noopener noreferrer"&gt;Digit-One&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.consolidate.eu/" rel="noopener noreferrer"&gt;Consolidate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://boehlerbrothers.com/" rel="noopener noreferrer"&gt;BoehlerBrothers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can see, there are quite a few! I did not expect that when I have started this. But companies seem to like give back to the community.&lt;/p&gt;

&lt;p&gt;Over the years the expectation of what a host provides also shifted a bit. Today we also expect them to provide some food, so that nobody has to leave the meetup hungry. Also, as mentioned before, they often convince some of their employees to do a talk, which makes organization also much easier for us.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your turn!
&lt;/h2&gt;

&lt;p&gt;This is the most important advice in this article: &lt;strong&gt;If there is no meetup for a certain topic in your area, then do not be afraid to start one!&lt;/strong&gt; All it needs is to reach out to a few companies via email or get in touch with developers in your area via platform like &lt;a href="https://www.linkedin.com" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; (I guess that makes it even easier compared to when I started).&lt;/p&gt;

&lt;p&gt;The worst thing that can happen is that you stop organizing after a few tries, because nobody shows up. So as said, nothing to be afraid of.&lt;/p&gt;

</description>
      <category>meetup</category>
    </item>
    <item>
      <title>Test creation methods on steroids with named parameters</title>
      <dc:creator>Daniel Rotter</dc:creator>
      <pubDate>Fri, 01 Aug 2025 08:00:40 +0000</pubDate>
      <link>https://dev.to/danrot90/test-creation-methods-on-steroids-with-named-parameters-5gn6</link>
      <guid>https://dev.to/danrot90/test-creation-methods-on-steroids-with-named-parameters-5gn6</guid>
      <description>&lt;p&gt;In &lt;a href="///2024/10/19/writing-high-quality-tests.html"&gt;my previous blog post about writing high quality tests&lt;/a&gt; I mentioned that writing test code in the most minimalistic way will improve maintainability in the long run by a lot. One pattern that helps with that is the &lt;a href="http://xunitpatterns.com/Creation%20Method.html" rel="noopener noreferrer"&gt;creation method pattern&lt;/a&gt;, especially when the object to be created is complex to set up and some of the required attributes are not important for the test at hand. Also, it helps to avoid using the constructor of objects directly making it a lot easier to adjust the tests when the constructor changes.&lt;/p&gt;

&lt;p&gt;The original description of the creation method pattern has some variations, the two most important ones for this blog post being:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Anonymous Creation Method&lt;/li&gt;
&lt;li&gt;Parameterized Creation Method&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the examples of the pattern description there are methods like &lt;code&gt;createAnonymousCustomer&lt;/code&gt; or &lt;code&gt;createCreditworthyCustomer&lt;/code&gt;, which would look something like this in PHP:&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;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;createAnonymousCustomer&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;Customer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'John'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Doe'&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;createCreditworthyCustomer&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;$firstName&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;$lastName&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;Customer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$firstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$lastName&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;The &lt;code&gt;createCreditworthyCustomer&lt;/code&gt; in that example just sets a third parameter to be true, which indicates that the customer is creditworthy. There would probably be better solutions to this problem, but I want to keep it simple.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is already quite nice, because it allows you to write tests without having to set all &lt;code&gt;Customer&lt;/code&gt; parameters manually even if you don't care about them. However, the number of methods required can easily explode, depending on which set of parameters you want to set in your tests. Maybe you also want to have a test creating an anomymous creditworthy customer, or a customer with a bank account but the name of the customer does not matter for the test (for which reason you don't want to mention any names in the test to keep it minimal).&lt;/p&gt;

&lt;p&gt;The good news is that it is relatively easily possible to cover all those use cases if you are using a language that supports &lt;a href="https://en.wikipedia.org/wiki/Named_parameter" rel="noopener noreferrer"&gt;named parameters&lt;/a&gt; like e.g. &lt;a href="https://www.php.net/manual/en/functions.arguments.php#functions.named-arguments" rel="noopener noreferrer"&gt;PHP&lt;/a&gt;. In that case you can have just one &lt;code&gt;createCustomer&lt;/code&gt; method handling all fields of the customer, give each parameter a default value, and then only set the ones you need in a single test:&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;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;createCustomer&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;$firstName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'John'&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;$lastName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Doe'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nv"&gt;$creditworthy&lt;/span&gt; &lt;span class="o"&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;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$firstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$lastName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$creditworthy&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;Depending on how often this method is needed, it can be placed as a private method on a test class or implemented using a &lt;a href="https://www.php.net/manual/en/language.oop5.traits.php" rel="noopener noreferrer"&gt;trait&lt;/a&gt;, so that it can be reused in multiple different test classes.&lt;/p&gt;

&lt;p&gt;And now it is really easy to use that creation method in your tests without passing all parameters every time (which is not so bad with 3 constructor parameters, but imagine there are more).&lt;/p&gt;

&lt;p&gt;If there is a test testing if the invoice is correctly generated you probably might want to assert if the right name is used. In that case you set the &lt;code&gt;firstName&lt;/code&gt; and &lt;code&gt;lastName&lt;/code&gt; parameters of the &lt;code&gt;createCustomer&lt;/code&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="cp"&gt;&amp;lt;?php&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;testGenerateInvoice&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$customer&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;createCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Daniel'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lastName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Rotter'&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In another case you might want to check if customers not being creditworthy are rejected:&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;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testRejectNonCreditworthyCustomer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$customer&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;createCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;creditworthy&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="c1"&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 if your test just needs a customer because another object requires it, but you don't care about the values at all, you can create an anonymous customer by calling the method without any parameters (and you should do so, to convey to the reader that the test does not care about the customer's attributes):&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;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testOrderMinimumQuantity&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$customer&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;createCustomer&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see this allows to have a minimal implementation effort (only one creation method per class), while still keeping all the advantages of that pattern. Happy testing!&lt;/p&gt;

</description>
      <category>testing</category>
      <category>php</category>
    </item>
    <item>
      <title>Batch curl requests in PHP using multi handles</title>
      <dc:creator>Daniel Rotter</dc:creator>
      <pubDate>Tue, 29 Apr 2025 10:56:07 +0000</pubDate>
      <link>https://dev.to/danrot90/batch-curl-requests-in-php-using-multi-handles-3jg6</link>
      <guid>https://dev.to/danrot90/batch-curl-requests-in-php-using-multi-handles-3jg6</guid>
      <description>&lt;p&gt;Recently I had a task at work, in which I had to send a decent amount of HTTP requests within a script. Naturally, one of the first ideas was to use a &lt;a href="https://en.wikipedia.org/wiki/Batch_processing" rel="noopener noreferrer"&gt;batching mechanism&lt;/a&gt; to send multiple requests in parallel. However, there was no resource with some real world (whatever that means) examples using PHP, so I did what I usually do in these cases: Figuring it out myself and blog about if afterwards.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Some context: I am using PHP 8.4.5 on my MacBook Pro 16" with an Apple M2 Pro to run the scripts in this blog post. I ran the scripts multiple times and always publish the fastest run.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sequential processing
&lt;/h2&gt;

&lt;p&gt;The easiest way to send multiple requests using a PHP script is to loop over all URLs and use the following steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Call &lt;a href="https://www.php.net/manual/en/function.curl-init.php" rel="noopener noreferrer"&gt;&lt;code&gt;curl_init&lt;/code&gt;&lt;/a&gt; to retrieve a handle&lt;/li&gt;
&lt;li&gt;Set some options on that handle using &lt;a href="https://www.php.net/manual/en/function.curl-setopt.php" rel="noopener noreferrer"&gt;&lt;code&gt;curl_setopt&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Send the request with &lt;a href="https://www.php.net/manual/en/function.curl-exec.php" rel="noopener noreferrer"&gt;&lt;code&gt;curl_exec&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the first naive implementation would probably result in something 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="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="nv"&gt;$urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/3'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/4'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/5'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/6'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/7'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/8'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/9'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="nv"&gt;$total_start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;microtime&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="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$urls&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$request_start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;microtime&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="nv"&gt;$handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;curl_init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_RETURNTRANSFER&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="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;curl_exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$handle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$response_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;curl_getinfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CURLINFO_HTTP_CODE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$request_duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;number_format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;microtime&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="o"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;$request_start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Response code for request to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$response_code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; in &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$request_duration&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&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;$total_duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;number_format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;microtime&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="o"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;$total_start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"All requests took &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$total_duration&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&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 output of this script looks something like this on my machine (I have used the &lt;code&gt;example.com&lt;/code&gt; domain as demonstrations like this is their purpose, but unfortunately everything except the root path returns a status code of &lt;code&gt;404&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ php 01_sequential_processing.php
Response code for request to https://example.com/ is 200 in 0.414s
Response code for request to https://example.com/1 is 404 in 0.414s
Response code for request to https://example.com/2 is 404 in 0.447s
Response code for request to https://example.com/3 is 404 in 0.453s
Response code for request to https://example.com/4 is 404 in 0.496s
Response code for request to https://example.com/5 is 404 in 0.418s
Response code for request to https://example.com/6 is 404 in 0.433s
Response code for request to https://example.com/7 is 404 in 0.465s
Response code for request to https://example.com/8 is 404 in 0.470s
Response code for request to https://example.com/9 is 404 in 0.415s
All requests took 4.427s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So this script needs about 4.4 seconds to execute 10 requests. This is the baseline without any optimizations. The next sections will try to improve this naive implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reusing the curl handle
&lt;/h2&gt;

&lt;p&gt;A very simple but yet quite effective optimization is to move the initialization of the curl handle before the loop. This way we are initializing the curl handle only once, and use the same curl handle to send all of our requests. In addition to the &lt;code&gt;curl_init&lt;/code&gt; call all hard coded curl options can also be moved before the loop, which is in this case the &lt;code&gt;CURLOPT_RETURNTRANSFER&lt;/code&gt; option causing &lt;code&gt;curl_exec&lt;/code&gt; to return the response instead of directly outputting it.&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="nv"&gt;$urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/3'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/4'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/5'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/6'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/7'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/8'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/9'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="nv"&gt;$total_start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;microtime&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="nv"&gt;$handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;curl_init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_RETURNTRANSFER&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="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$urls&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$request_start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;microtime&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="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;curl_exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$handle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$response_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;curl_getinfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CURLINFO_HTTP_CODE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$request_duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;number_format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;microtime&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="o"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;$request_start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Response code for request to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$response_code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; in &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$request_duration&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&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;$total_duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;number_format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;microtime&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="o"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;$total_start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"All requests took &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$total_duration&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When running this version, &lt;strong&gt;you might already realize that it runs much faster than the previous one&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ php 02_reuse_handles.php
Response code for request to https://example.com/ is 200 in 0.400s
Response code for request to https://example.com/1 is 404 in 0.130s
Response code for request to https://example.com/2 is 404 in 0.135s
Response code for request to https://example.com/3 is 404 in 0.130s
Response code for request to https://example.com/4 is 404 in 0.144s
Response code for request to https://example.com/5 is 404 in 0.212s
Response code for request to https://example.com/6 is 404 in 0.139s
Response code for request to https://example.com/7 is 404 in 0.130s
Response code for request to https://example.com/8 is 404 in 0.134s
Response code for request to https://example.com/9 is 404 in 0.133s
All requests took 1.688s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As it seems, initializing such a handle seems to be quite a heavy task. I know that executing something just once (or even multiple times, as I did on these examples) is not a good benchmark, but a perfomance improvement of over 60% is still substantial.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sending curl requests in parallel
&lt;/h2&gt;

&lt;p&gt;Even with the latter improvement, all of the requests are running in sequence, i.e. one request has to finish before the next one can start. It would be much faster, if all these requests would run in parallel. Luckily, PHP comes with some methods prefixed with &lt;code&gt;curl_multi_&lt;/code&gt;, which enable developers to do exactly that. However, I found all those methods a bit confusing, which was probably the main motivation to write this blog post.&lt;/p&gt;

&lt;p&gt;So if we want to parallelize all requests from the previous examples, the code would look like the following:&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="nv"&gt;$urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/3'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/4'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/5'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/6'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/7'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/8'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/9'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="nv"&gt;$total_start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;microtime&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="nv"&gt;$multi_handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;curl_multi_init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$handles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&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="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$urls&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$handles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;curl_init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$handles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_RETURNTRANSFER&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="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$handles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$urls&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="nb"&gt;curl_multi_add_handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$multi_handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$handles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$running&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="k"&gt;do&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;curl_multi_exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$multi_handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$running&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;curl_multi_select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$multi_handle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$running&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&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="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$urls&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&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;$response_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;curl_getinfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$handles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="no"&gt;CURLINFO_HTTP_CODE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Response code for request to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$urls&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$response_code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&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;$total_duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;number_format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;microtime&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="o"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;$total_start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"All requests took &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$total_duration&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&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 main difference to the previous examples are all these calls to the &lt;code&gt;curl_multi_&lt;/code&gt; functions. The following parts are the most interesting ones of this code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://www.php.net/manual/en/function.curl-multi-init.php" rel="noopener noreferrer"&gt;&lt;code&gt;curl_multi_init&lt;/code&gt;&lt;/a&gt; method initializes a curl multi handle, to which we can add multiple usual curl handles, and execute them in parallel later.&lt;/li&gt;
&lt;li&gt;In the &lt;code&gt;for&lt;/code&gt; loop curl handles are created, set up, and then added to the curl multi handle using &lt;a href="https://www.php.net/manual/en/function.curl-multi-add-handle.php" rel="noopener noreferrer"&gt;&lt;code&gt;curl_multi_add_handle&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;do-while&lt;/code&gt; loop is then responsible for actually executing those requests, and that part was the most confusing to me. &lt;a href="https://www.php.net/manual/en/function.curl-multi-exec.php" rel="noopener noreferrer"&gt;&lt;code&gt;curl_multi_exec&lt;/code&gt;&lt;/a&gt; executes all those requests, but it does not block, instead the second parameter is a reference that can be interpreted as a flag indicating if some requests are still running. This flag is used as the loop condition, however, &lt;strong&gt;without another call to &lt;a href="https://www.php.net/manual/en/function.curl-multi-select.php" rel="noopener noreferrer"&gt;&lt;code&gt;curl_multi_select&lt;/code&gt;&lt;/a&gt; this would be a &lt;a href="https://en.wikipedia.org/wiki/Busy_waiting" rel="noopener noreferrer"&gt;busy wait&lt;/a&gt;&lt;/strong&gt;. &lt;code&gt;curl_multi_select&lt;/code&gt; is actually the blocking operation, which blocks until any of the multi handle's requests has made progress. To me it feels weird that this is happening for any request, especially since the method does not tell which one has made progress. This is the reason for still putting this in a loop, which means that after the loop all requests will have finished.&lt;/li&gt;
&lt;li&gt;Finally there is another &lt;code&gt;for&lt;/code&gt; loop which will output some message. In here the usual curl handles (not the multi handle!) must be used again. In this example this is &lt;code&gt;curl_getinfo&lt;/code&gt; again to retrieve the status code for each handle. Interestingly, if you would want to get the content of a request, you have to use &lt;a href="https://www.php.net/manual/en/function.curl-multi-getcontent.php" rel="noopener noreferrer"&gt;&lt;code&gt;curl_multi_getcontent&lt;/code&gt;&lt;/a&gt; and also pass the curl handle for a single request. That's probably because &lt;code&gt;curl_exec&lt;/code&gt; usually returns the response body, something that is not working with multi handles, since &lt;code&gt;curl_exec&lt;/code&gt; is not called in the code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even though this works, it is kind of a weird API in my opinion. However, the following output shows that its usage pays off in terms of performance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ php 03_multi_curl.php
Response code for request to https://example.com/ is 200
Response code for request to https://example.com/1 is 404
Response code for request to https://example.com/2 is 404
Response code for request to https://example.com/3 is 404
Response code for request to https://example.com/4 is 404
Response code for request to https://example.com/5 is 404
Response code for request to https://example.com/6 is 404
Response code for request to https://example.com/7 is 404
Response code for request to https://example.com/8 is 404
Response code for request to https://example.com/9 is 404
All requests took 0.499s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;This is even much faster than the previous example, taking a third of the time on my machine.&lt;/strong&gt; It approximately takes the amount of time the slowest of all requests takes, because by then all other requests will already have finished.&lt;/p&gt;

&lt;h2&gt;
  
  
  Batching parallelized curl requests
&lt;/h2&gt;

&lt;p&gt;But unfortunately there is a limit to this: you cannot start an infinite amount of requests at the same time. Apparently my machine can handle 10 requests in parallel, but starting with some number of requests it would probably make sense to batch requests. The following example is going to assume that this number is 3, i.e. we will only send 3 requests at a time. In addition, the curl handles should be reused like in our first step of improvement.&lt;/p&gt;

&lt;p&gt;The following code shows how to implement such a batching mechanism with curl in PHP:&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="nv"&gt;$urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/3'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/4'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/5'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/6'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/7'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/8'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'https://example.com/9'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="nv"&gt;$total_start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;microtime&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="nv"&gt;$multi_handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;curl_multi_init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$batch_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$handles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&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="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nv"&gt;$batch_size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$handles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;curl_init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$handles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_RETURNTRANSFER&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="nv"&gt;$batch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&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;$urls&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$index&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$batch&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$url&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="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$batch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nv"&gt;$batch_size&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$index&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$urls&lt;/span&gt;&lt;span class="p"&gt;)&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="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&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="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$batch&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;curl_multi_add_handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$multi_handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$handles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$handles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$batch&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nv"&gt;$running&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="k"&gt;do&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;curl_multi_exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$multi_handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$running&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;curl_multi_select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$multi_handle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$running&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&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="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$batch&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$response_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;curl_getinfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$handles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="no"&gt;CURLINFO_HTTP_CODE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Response code for request to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$batch&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$response_code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nb"&gt;curl_multi_remove_handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$multi_handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$handles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nv"&gt;$batch&lt;/span&gt; &lt;span class="o"&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;$total_duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;number_format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;microtime&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="o"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;$total_start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"All requests took &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$total_duration&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&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 structure is a bit different than the previous examples, even though there is only one new method (&lt;code&gt;curl_multi_remove_handle&lt;/code&gt;) being used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first &lt;code&gt;for&lt;/code&gt; loop sets up three curl handles that will be used for all requests being sent.&lt;/li&gt;
&lt;li&gt;Then the first part of the &lt;code&gt;foreach&lt;/code&gt; is responsible for batching (up until the &lt;code&gt;continue&lt;/code&gt; statement, which prevents the following code to be executed if the batch is not properly filled).&lt;/li&gt;
&lt;li&gt;If a batch is filled, another &lt;code&gt;for&lt;/code&gt; loop will add the correct number of handles to the multi handle and set the URL for that handle.&lt;/li&gt;
&lt;li&gt;Afterwards the same loop with &lt;code&gt;curl_multi_exec&lt;/code&gt; and &lt;code&gt;curl_multi_select&lt;/code&gt; as in the previous example is used to execute
the requests for the current batch.&lt;/li&gt;
&lt;li&gt;The following &lt;code&gt;for&lt;/code&gt; loop outputs the information and removes the handle from the multi handle using &lt;a href="https://www.php.net/manual/en/function.curl-multi-remove-handle.php" rel="noopener noreferrer"&gt;&lt;code&gt;curl_multi_remove_handle&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It might be tempting to call &lt;code&gt;curl_multi_add_handle&lt;/code&gt; just once for each handle in the first &lt;code&gt;for&lt;/code&gt; loop where they are being initialized and do not call &lt;code&gt;curl_multi_remove_handle&lt;/code&gt; after each batch in order to further improve performance. However, it turns out that this would have an undesired side effect: For some reason &lt;code&gt;curl_multi_exec&lt;/code&gt; will then reuse the information from the already received responses, i.e. there would only 3 requests being sent instead of the necessary 10. Therefore these repeated calls to &lt;code&gt;curl_multi_add_handle&lt;/code&gt; and &lt;code&gt;curl_multi_remove_handle&lt;/code&gt; are absolutely necessary.&lt;/p&gt;

&lt;p&gt;The result is not as fast as the previous example, since this approach only makes sense if many more requests are being sent, and not all of them can be sent at the same time. Still, here are the numbers:&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="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;php&lt;/span&gt; &lt;span class="mo"&gt;04_&lt;/span&gt;&lt;span class="n"&gt;chunking_parallel_requests&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;php&lt;/span&gt;
&lt;span class="nc"&gt;Response&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="o"&gt;://&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
&lt;span class="nc"&gt;Response&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="o"&gt;://&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;
&lt;span class="nc"&gt;Response&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="o"&gt;://&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;
&lt;span class="nc"&gt;Response&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="o"&gt;://&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;
&lt;span class="nc"&gt;Response&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="o"&gt;://&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;
&lt;span class="nc"&gt;Response&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="o"&gt;://&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;
&lt;span class="nc"&gt;Response&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="o"&gt;://&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;
&lt;span class="nc"&gt;Response&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="o"&gt;://&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;
&lt;span class="nc"&gt;Response&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="o"&gt;://&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;
&lt;span class="nc"&gt;Response&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="o"&gt;://&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;
&lt;span class="nc"&gt;All&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt; &lt;span class="n"&gt;took&lt;/span&gt; &lt;span class="mf"&gt;0.801&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the bottom line is that parallelizing the HTTP requests can result in a substantial performance improvement. I hope that my findings can help others struggling with the same issues.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>php</category>
    </item>
    <item>
      <title>Automatically generate changelogs with git</title>
      <dc:creator>Daniel Rotter</dc:creator>
      <pubDate>Thu, 03 Apr 2025 10:45:58 +0000</pubDate>
      <link>https://dev.to/danrot90/automatically-generate-changelogs-with-git-1l53</link>
      <guid>https://dev.to/danrot90/automatically-generate-changelogs-with-git-1l53</guid>
      <description>&lt;p&gt;One of the rather tedious tasks of a developer is to generate changelogs. I cannot imagine that anybody enjoys going through the project history and try to reverse engineer what has happened since the last release. But the good news is that with a bit of discipline it is quite straightforward to generate those changelogs from your version control history. The examples in this blog post will use &lt;a href="https://git-scm.com/" rel="noopener noreferrer"&gt;git&lt;/a&gt;, but I guess every (mature) version control system will have similar commands as the ones being used here.&lt;/p&gt;

&lt;p&gt;The explained process only works if some rules are followed while using git:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All code in your &lt;code&gt;main&lt;/code&gt; or &lt;code&gt;master&lt;/code&gt; branch is contributed by merging &lt;a href="https://docs.github.com/en/pull-requests" rel="noopener noreferrer"&gt;GitHub pull
requests&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The commits of pull requests are squashed during the commit&lt;/li&gt;
&lt;li&gt;The title of your pull requests are written well enough to be used as changelog entries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;You might make the general idea also work in other circumstances, as long as there is at least some consistency, but this post assumes these pre-conditions.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This process basically enables developers to use the version control history as a changelog. Squashing is not absolutely necessary, but I have yet to work in a team where the commit messages without squashing are written well enough for this to work, although it is theorethically possible. I guess it is just unlikely that people also review the commit messages in a pull request.&lt;/p&gt;

&lt;p&gt;Anyway, when following these rules the output of the &lt;a href="https://git-scm.com/docs/git-log" rel="noopener noreferrer"&gt;&lt;code&gt;git log&lt;/code&gt; command&lt;/a&gt; already looks quite promising (I pasted only the first part of the output):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git log
commit f582a707e7b28aa444a737146c0626810242fd13 (HEAD -&amp;gt; master, danrot/master, danrot/HEAD)
Author: Daniel Rotter &amp;lt;daniel.rotter@gmail.com&amp;gt;
Date:   Wed Feb 5 22:16:45 2025 +0100

    Write blog posts about indirections (#46)

commit bd7d3bbf43412926ccaeafbe63219366e6b21123
Author: Daniel Rotter &amp;lt;daniel.rotter@gmail.com&amp;gt;
Date:   Sun Nov 24 15:02:48 2024 +0100

    Fix images when CSS is not loaded (#45)

commit 1e5932c0d83099fba9ed467aab5ed3f3e406394e
Author: Daniel Rotter &amp;lt;daniel.rotter@gmail.com&amp;gt;
Date:   Sun Nov 24 11:20:57 2024 +0100

    Fix sitemap in robots.txt (#44)

commit 449ae6c390d083879b5b139954e3eda04ce36b2c
Author: Daniel Rotter &amp;lt;daniel.rotter@gmail.com&amp;gt;
Date:   Sat Nov 23 19:37:32 2024 +0100

    Add 404 page (#43)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first thing that needs fixing is returning the correct list of commits, since the &lt;code&gt;git log&lt;/code&gt; command will just return all commits within the current repository. In order to do that, we can pass a so-called &lt;a href="https://git-scm.com/docs/gitrevisions" rel="noopener noreferrer"&gt;revision range&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git log 1e5932c0d83099fba9ed467aab5ed3f3e406394e..HEAD
commit f582a707e7b28aa444a737146c0626810242fd13 (HEAD -&amp;gt; master, danrot/master, danrot/HEAD)
Author: Daniel Rotter &amp;lt;daniel.rotter@gmail.com&amp;gt;
Date:   Wed Feb 5 22:16:45 2025 +0100

    Write blog posts about indirections (#46)

commit bd7d3bbf43412926ccaeafbe63219366e6b21123
Author: Daniel Rotter &amp;lt;daniel.rotter@gmail.com&amp;gt;
Date:   Sun Nov 24 15:02:48 2024 +0100

    Fix images when CSS is not loaded (#45)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;1e5932c0d83099fba9ed467aab5ed3f3e406394e..HEAD&lt;/code&gt; notation includes all commits that can be reached from &lt;code&gt;HEAD&lt;/code&gt; (i.e. the commit you are currently on) but not from &lt;code&gt;1e5932c0d83099fba9ed467aab5ed3f3e406394e&lt;/code&gt;, for which reason this time only two commits are shown. Keep in mind that this notation can also be used with tags, so if you have a tag for your last release you could also use something like &lt;code&gt;1.0..HEAD&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For changelogs we probably do not need most of the information included in the default output of the &lt;code&gt;git log&lt;/code&gt; command. Luckily, there is a &lt;code&gt;--format&lt;/code&gt; option that allows to configure the output. In this example we only want to use the subject line of the squashed merge commit, which will be the GitHub pull request title. For that we can use the &lt;code&gt;%s&lt;/code&gt; placeholder, which stands for the subject line of a commit message.&lt;/p&gt;

&lt;p&gt;So, based on the example above, the following command can be used to get only the subject lines of all commits for a given revision range:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git log 1e5932c0d83099fba9ed467aab5ed3f3e406394e..HEAD --format=%s
Write blog posts about indirections (#46)
Fix images when CSS is not loaded (#45)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depending on the structure of the project, this might already be good enough. Just replace the commit hash with another commit hash or a tag each time the changelog is generated.&lt;/p&gt;

&lt;p&gt;However, consider that this really includes all commits. Especially if you are using a branching model like &lt;a href="https://nvie.com/posts/a-successful-git-branching-model/" rel="noopener noreferrer"&gt;git flow&lt;/a&gt; the version history includes commits that might not be that interesting for a changelog, e.g. commits merging in a &lt;code&gt;develop&lt;/code&gt; branch. One way of fixing this is to use &lt;code&gt;grep&lt;/code&gt; (or a similar tool) to filter out unwanted messages. This depends a bit on the way you are doing things, but one way this could be solved if you are using GitHub, is to filter for a pull request number at the end of the commit message (e.g. the "(#46)" part in the last commit shown above). Manual commits usually do not have that part, which means we can filter for that using an extended regular expression (which forces us to use the &lt;code&gt;-E&lt;/code&gt; parameter of &lt;code&gt;grep&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git log 1e5932c0d83099fba9ed467 aab5ed3f3e406394e..HEAD --format="%s" | grep -E '\(#[0-9]+\)$'
Write blog posts about indirections (#46)
Fix images when CSS is not loaded (#45)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The regular expression &lt;code&gt;\(#[0-9]+\)$&lt;/code&gt; search for literal &lt;code&gt;(&lt;/code&gt;(which must be escaped by a backslash), followed by a &lt;code&gt;#&lt;/code&gt;. Then &lt;code&gt;[0-9]+&lt;/code&gt; searches for at least one digit. Then there should be another literal &lt;code&gt;)&lt;/code&gt;(again escaped by a backslash). Finally the regular expression ends with a &lt;code&gt;$&lt;/code&gt;, which means that after the expression the line must be finished, which causes to not match such a pattern at the beginning or in the middle of the commit messages's subject line.&lt;/p&gt;

&lt;p&gt;Last but not least, you can make use of &lt;code&gt;sed&lt;/code&gt; to bring these commit messages in a format that you can directly copy e.g. into a markdown file and do some slight adjustments. Personally I like to have the pull request number at the beginning. All of this can be achived using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git log 1e5932c0d83099fba9ed467aab5ed3f3e406394e..HEAD --format="%s" | grep -E '\(#[0-9]+\)$' | sed -E 's/(.*)\((.*)\)/- \2 \1/'
- #46 Write blog posts about indirections 
- #45 Fix images when CSS is not loaded
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only part that changed compared to the previous example is the last part using &lt;code&gt;sed&lt;/code&gt;. Again, &lt;code&gt;-E&lt;/code&gt; lets us use extended regular expressions. The passed argument is key to what we are trying to do here. &lt;a href="https://en.wikipedia.org/wiki/Sed" rel="noopener noreferrer"&gt;&lt;code&gt;sed&lt;/code&gt;&lt;/a&gt; stands for stream editor, and the passed argument is a small script that &lt;code&gt;sed&lt;/code&gt; executes on its input. The &lt;code&gt;s&lt;/code&gt; part of the command tells &lt;code&gt;sed&lt;/code&gt; that it should do a search and replace operation, whereby &lt;code&gt;/&lt;/code&gt; is used a delimiter. So &lt;code&gt;s/foo/bar&lt;/code&gt; would replace all occurences of &lt;code&gt;foo&lt;/code&gt; with &lt;code&gt;bar&lt;/code&gt;. It gets really interesting when you use capturing groups (this is done by placing something into brackets), since you can make use of the capturing group in the replace part of the operation. This is done by using a backslash followed by the number of the capture group.&lt;/p&gt;

&lt;p&gt;With that knowledge, let's break down the &lt;code&gt;sed&lt;/code&gt; operation &lt;code&gt;s/(.*)\((.*)\)/- \2 \1/&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;s&lt;/code&gt; starts the search and replace operation followed by a &lt;code&gt;/&lt;/code&gt; as a delimiter&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;(.*)\((.*)\)&lt;/code&gt; defines two capturing groups by using the unescaped brackets

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;(.*)&lt;/code&gt; looks for an arbitrary number (&lt;code&gt;*&lt;/code&gt;) of any character (&lt;code&gt;.&lt;/code&gt;) and stores it in a capturing group, which then contains most of the commit message's subject line&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;\((.*)\)&lt;/code&gt; looks for literal brackets in the texts (&lt;code&gt;\(&lt;/code&gt; and &lt;code&gt;\)&lt;/code&gt;), which also contains an arbitray text (&lt;code&gt;.*&lt;/code&gt;), but only the text without the enclosing brackets will be stored in the second capturing group, which is the number referencing the pull request&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;The &lt;code&gt;- \2 \1&lt;/code&gt; enclosed in slashes replaces the search result with a literal &lt;code&gt;-&lt;/code&gt; (which stands for a list in markdown) followed by the second capturing group (the commit message) and the first capturing group (the pull request number).&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;As you can see, the command line allows you to write relatively complex one-liners. However, that is exactly what I like about it. Using that command you can now generate your changelog from a (relatively structured) git commit history, without the need for any additional tools (&lt;code&gt;grep&lt;/code&gt; and &lt;code&gt;sed&lt;/code&gt; are available on almost all unix-based systems, and when you develop software you most likely have &lt;code&gt;git&lt;/code&gt; installed as well).&lt;/p&gt;

</description>
      <category>git</category>
    </item>
    <item>
      <title>The problem with indirections</title>
      <dc:creator>Daniel Rotter</dc:creator>
      <pubDate>Wed, 26 Feb 2025 15:06:29 +0000</pubDate>
      <link>https://dev.to/danrot90/the-problem-with-indirections-1iii</link>
      <guid>https://dev.to/danrot90/the-problem-with-indirections-1iii</guid>
      <description>&lt;p&gt;Almost any program we write makes heavy use of different kind of indirections, at least if it should be remotely useful. Even though they are so fundamental to the everyday work of a software developer, we should always carefully consider in which extent we want to use them, and more often than not I see certain indirections be heavily overused, which can easily lead to hard-to-understand code.&lt;/p&gt;

&lt;p&gt;But what exactly is an indirection? I like to think of it as something that hides something from me respectively something that requires me to look something up somewhere else in order to understand the result.&lt;/p&gt;

&lt;h2&gt;
  
  
  A overly simplified example
&lt;/h2&gt;

&lt;p&gt;I have this topic on my mind for quite some time already, and recently I ran into &lt;a href="https://www.linkedin.com/posts/clemenshelm_ich-finde-diese-l%C3%B6sung-gro%C3%9Fartig-warum-activity-7262040621786259456-9AKz/" rel="noopener noreferrer"&gt;this post from Clemens Helm&lt;/a&gt;, which finally motivated me to write about it. I think that meme was originally posted on &lt;a href="https://www.reddit.com/r/ProgrammerHumor/comments/1ax8zh6/taskdone/" rel="noopener noreferrer"&gt;r/ProgrammerHumor&lt;/a&gt;. Somebody asks for help with the task "print numbers from 1 to 25 in a clockwise expanding spiral from center", and the other person responds with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;21 22 23 24 25&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;20  7  8  9 10&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;19  6  1  2 11&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;18  5  4  3 12&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;17 16 15 14 13&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As Clemens has pointed out, this is kind of the best solution to the problem. Unless you need to print a spiral with another value than 25, it does not make any sense to invest more time into this. However, quite often developers try to come up with clever solutions – which are certainly fun to implement (otherwise you kind of miss the point of being a developer) – even if a much simpler one would also do the job. This is commonly known as &lt;a href="https://en.wikipedia.org/wiki/Overengineering" rel="noopener noreferrer"&gt;over-engineering&lt;/a&gt;. There is also the &lt;a href="https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it" rel="noopener noreferrer"&gt;YAGNI (You aren't gonna need it) principle&lt;/a&gt;, which advices against writing code in a more complicated manner than absolutely necessary. Sticking with the previous example, this means that you should not write some complex algorithm if all you ever will do is calling that algorithm with a single value. If you fail to follow these principles, it will lead to lots of unnecessary code that needs additional maintaining and is harder to understand compared to code that is kept simple.&lt;/p&gt;

&lt;p&gt;Now, as said, this example is overly simplified, but I want to show you some more examples of indirections that might sometimes even hurt the maintainability of your code. Of course &lt;strong&gt;the usage of certain patterns also comes with advantages, but carefully consider whether or not the advantages outweigh the additional baggage they come with&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I will also show how some frameworks introduce additional indirections that might be cumbersome, especially when you try to debug your application. In the case of frameworks you also have to keep in mind that in addition to these indirections, you are also vulnerable to breaking changes on updates, which is a serious drawback that should not be taken lightly. So when choosing a framework make sure that it gives you more in return than you might pay in the long run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Variables
&lt;/h2&gt;

&lt;p&gt;Probably the most used indirection is using a variable. Of course variables make a lot of sense in many cases, without them it would be impossible to build anything remotely dynamic. They might also make sense &lt;a href="///2021/01/16/code-comments-are-mostly-a-violation-of-dry.html"&gt;to give something a name to avoid adding an additional comment&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;However, there are also situations in which using a variable might make code more complicated to read. This is the case when there is no variation in the value at all, and it is pretty much self-explanatory. The following example shows code that is relatively hard to understand when you think about what it actually does.&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="nv"&gt;$greeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Hello'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Daniel'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$punctuation&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="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$greeting&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$name$punctuation&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, if any of these parts could change due to external circumstances using variables makes total sense, but in that case the code could be written much simpler by only putting the name into a variable (assuming that this is the only part that might change).&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="nv"&gt;$name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Daniel'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Hello &lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="s2"&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 there is no need to look and maybe even jump to a completely different location to find the value of a variable (the above example is still pretty simple, but the different variables might not be located right next to each other in code). Therefore I am trying not to introduce variables unless they really serve some purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Methods and functions
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://refactoring.com/catalog/extractFunction.html" rel="noopener noreferrer"&gt;Extracting a function&lt;/a&gt; (or method) is one of the first ideas people have when writing DRY code, since it helps a lot with not duplicating any information. And of course, this is a good thing! However, keep in mind that when the code is read by another developer (or yourself in a few months) they probably have to follow that function and also try to understand what it is doing when they are looking for a bug. So a method should only be extracted when it either helps with readability or the code is actually used in multiple places.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Something that has always bothered me a bit is when rules are blindly followed.&lt;/strong&gt; One such rule I remember is "avoid nesting", which is usually done to have methods with a lower &lt;a href="https://en.wikipedia.org/wiki/Cyclomatic_complexity" rel="noopener noreferrer"&gt;cyclomatic complexity&lt;/a&gt;. While it would be nice to have an objective number telling if a function is good or bad, I am absolutely sure that solely relying on cyclomatic complexity is a bad idea. One situation in which I have experienced such a rule was when I had to write code reading an XML file that had a structure that forced me to use nested loops. I spare you the details, since I don't like XML (does anybody use that nowadays anyway?) and role with a much simpler example.&lt;/p&gt;

&lt;p&gt;Let's say we want to write a method that output the content of a two-dimensional array. This kinda calls for a nested &lt;code&gt;for&lt;/code&gt; loop.&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;function&lt;/span&gt; &lt;span class="n"&gt;printTable&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;$table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&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="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&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;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$j&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="nv"&gt;$j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="nv"&gt;$j&lt;/span&gt;&lt;span class="o"&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;echo&lt;/span&gt; &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nv"&gt;$j&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="kc"&gt;PHP_EOL&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;I think this code is pretty straightforward to read and understand, at least as long as it stays that simple. However, if you work with somebody who has an unreasonable fear of nested &lt;code&gt;for&lt;/code&gt; loops they will make you write code 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="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;printTable&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;$table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&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="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;printRow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="kc"&gt;PHP_EOL&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;function&lt;/span&gt; &lt;span class="n"&gt;printRow&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;$row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$j&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="nv"&gt;$j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$row&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nv"&gt;$j&lt;/span&gt;&lt;span class="o"&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;echo&lt;/span&gt; &lt;span class="nv"&gt;$row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$j&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;Now you have introduced an indirection, which makes the code harder to follow, especially when the functions are a bit longer and they do not fit your screen. Instead of just reading code from top to bottom and easily understand what is happening you must jump to the definition of the &lt;code&gt;printRow&lt;/code&gt; function and go back after you have read it. And the worst part: &lt;strong&gt;The nested for loop is still there, so from a runtime perspective nothing has improved.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Again, of course there is some point at which it might make sense to extract a method, but doing it too early is something I would consider premature optimization, which can make code even harder to read.&lt;/p&gt;

&lt;p&gt;Another example is usage of the &lt;a href="https://docs.phpunit.de/en/12.0/fixtures.html#fixtures" rel="noopener noreferrer"&gt;&lt;code&gt;setUp&lt;/code&gt; method in xUnit frameworks like PHPUnit&lt;/a&gt;, which is even worse, since that call is happening implicitly, i.e. it is called before the actual test method, without the actual test method referencing it in any way. Let's have a look at the (stripped down) example from the official PHPUnit documentation (which is shown there just for demonstration purposes, they even address some of the issues):&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;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExampleTest&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TestCase&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;?Example&lt;/span&gt; &lt;span class="nv"&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="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testSomething&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;assertSame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'the-result'&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;example&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;setUp&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;example&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;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;createStub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Collaborator&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="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;Developers with experience in that testing framework will know about the setup method – especially when there is an otherwise uninitialized property. But if the test gets more complex it might not be that obvious. And when the test is written as follows, even developers without knowledge of the &lt;code&gt;setUp&lt;/code&gt; method can easily understand what's going on.&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;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExampleTest&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TestCase&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;testSomething&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;$example&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;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;createStub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Collaborator&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="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertSame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'the-result'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$example&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;doSomething&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;Again, I am not saying never use the &lt;code&gt;setUp&lt;/code&gt; method, I use them quite often myself (and probably would even do it in the above example). However, &lt;strong&gt;I always make sure to keep it minimal, which means most of the time I do the bare minimum to initialize the system under test&lt;/strong&gt;. That is because if there is too much code in the &lt;code&gt;setUp&lt;/code&gt; method it is very likely that not all tests need that code to be executed, which makes your tests slower to run and harder to understand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interfaces
&lt;/h2&gt;

&lt;p&gt;Similar to my complaint about blindly following rules regarding methods before, I also think that interfaces are sometimes overused. There is a saying "code against interfaces, not implementations", which is probably a big driver for usage of the &lt;code&gt;interface&lt;/code&gt; keyword. However, &lt;strong&gt;keep in mind that does not necessarily mean that you need an &lt;code&gt;interface&lt;/code&gt; keyword&lt;/strong&gt;, since also a class has its own implicit interface, even if it does not implement one.&lt;/p&gt;

&lt;p&gt;Also, the usage of interfaces has some disadvantages. First, it results in more code and therefore a higher complexity. It also forces you to write the same signature at least twice in your code, once in the interface itself and at least once in a class implementing that interface. This means you also need to update two places in case something about the interface changes (although most programming languages resp. static analyzers will happily tell you when these signatures do no match). Additionally, it also has some (negative) impact on the developer experience. &lt;strong&gt;If you have ever worked with a code base making heavy use of interfaces you know how cumbersome it is to always jump twice in your IDE to get to the implementation&lt;/strong&gt; (first to the interface itself, then to the implementation of the interface).&lt;/p&gt;

&lt;p&gt;Therefore I try to resist to introduce interfaces for all classes and only use them when it yields a real benefit. Some example coming to my mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Making use of &lt;a href="https://en.wikipedia.org/wiki/Polymorphism_(computer_science)" rel="noopener noreferrer"&gt;polymorphism&lt;/a&gt;, e.g. to loop over many different objects implementing the same interface&lt;/li&gt;
&lt;li&gt;Reverse the relationship between different packages or modules using the &lt;a href="https://en.wikipedia.org/wiki/Dependency_inversion_principle" rel="noopener noreferrer"&gt;dependency inversion principle&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Introducing multiple implementations of the same interface, e.g. to introduce &lt;a href="///2023/09/22/avoid-mocking-repositories-by-using-in-memory-implementations.html"&gt;an in-memory implementation of a repository for tests&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  CSS utility classes
&lt;/h2&gt;

&lt;p&gt;I want to start with a disclaimer: I am probably one of the biggest &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;tailwind&lt;/a&gt; opponent out there. This has many different reasons, but in this post I only want to discuss the impact of the indirections introduced by a utility-first CSS framework like tailwind.&lt;/p&gt;

&lt;p&gt;First of all, the installation process of tailwind is not trivial. While with plain CSS you just use one &lt;code&gt;link&lt;/code&gt; or &lt;code&gt;style&lt;/code&gt; tag (depending on you want the styles to be inlined in HTML or in a separate CSS file) the &lt;a href="https://tailwindcss.com/docs/installation" rel="noopener noreferrer"&gt;getting started page of tailwind&lt;/a&gt; lists five steps to get started, whereby in every step something could go wrong.&lt;/p&gt;

&lt;p&gt;In addition, &lt;strong&gt;any framework using CSS utility classes requires the developers to learn about those classes&lt;/strong&gt;, which seems like wasted time to me, especially when these utility classes are on such a low level that they do not abstract anything, and you still need to know how CSS works under the hood to use them. I rather learn the CSS properties instead, which will be useful for a much longer time than any classes introduced by a framework.&lt;/p&gt;

&lt;p&gt;Another drawback is that using such utility-first frameworks kinda break the dev tools, or at the very least make them less helpful. Take for instance a look at this HTML fragment I've taken from &lt;a href="https://tailwindcss.com/docs/installation" rel="noopener noreferrer"&gt;tailwind's installation page&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"relative flex text-slate-400 text-xs leading-6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-2 flex-none text-sky-300 border-t border-b border-t-transparent border-b-sky-300 px-4 py-1 flex items-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        src/index.html
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex-auto flex pt-2 rounded-tr-xl overflow-hidden"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex-auto -mr-px bg-slate-700/50 border border-slate-500/30 rounded-tl"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"absolute top-2 right-0 h-8 flex items-center pr-4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"relative flex -mr-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-slate-500 hover:text-slate-400"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- omitted the SVG for brevity--&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I would argue that &lt;strong&gt;it is not easily possible to tell the purpose of each element, yet this is what you see when you open the HTML inspector of your preferred browser&lt;/strong&gt;. It does not really look better for the styles shown in the developer tools, when you click on one of the elements you see a long list of classes with mostly a single CSS property. Both of these problems disappear when you use plain CSS in combination with semantic class names instead. Someone could argue that these classes are also a level of indirection, but at the very least they are not coming from a framework and they are in our control. So I think the following example is shorter, easier to understand, and there is no need to learn about another indirection (utility classes and their abbreviations in this example).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tabs-container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tab"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        src/index.html
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"empty-area-container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"empty-area"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icons"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"copy-to-clipboard-container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"copy-to-clipboard-button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- omitted the SVG for brevity--&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way I can apply the exact same styles as in the previous example, but when I am looking at the HTML inspector, which I find an invaluable tool, I have a much nicer time navigating the DOM tree. Also, when clicking on any element in the inspector, the list of applied styles is easier to read, since there are a lot less classes involved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencing classes using strings
&lt;/h2&gt;

&lt;p&gt;This is something I see a lot in Laravel, e.g. in its &lt;a href="https://laravel.com/docs/11.x/validation" rel="noopener noreferrer"&gt;Validation module&lt;/a&gt;. The following lines show how incoming request data can be validated in Laravel:&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="nv"&gt;$validated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validate&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="s1"&gt;'unique:posts'&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 is how you define in Laravel that the &lt;code&gt;title&lt;/code&gt; property must be unique among all posts. In my opinion encoding this information in just a simple string is not a good idea. The only advantage I see is its brevity, but this definitely does not outweigh its disadvantages. First of all &lt;strong&gt;you have to know the defined strings for validation rules, and I am not sure if any IDE supports auto-completing these values&lt;/strong&gt;. Additonally it is another level of indirection: &lt;strong&gt;If you want to have a look at the implementation, then you would have to look up the key (e.g. &lt;code&gt;required&lt;/code&gt;) somewhere manually, in order to figure out which file or class implements that rule.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Luckily Laravel also supports a cleaner version of the codee above:&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="nv"&gt;$validated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validated&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="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;Rule&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'posts'&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;When writing the code like this, every proper IDE with PHP support will not only apply proper syntax highlighting, but also enable you to jump to the definition of the validation rule. This is much more convenient when you want to figure out what those rules do exactly.&lt;/p&gt;

&lt;p&gt;Unfortunately, it is quite common for Laravel to optimize for brevity. This causes them to often recommend techniques I would consider bad practices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Magic getters and setter
&lt;/h2&gt;

&lt;p&gt;Another thing I realized while working with Laravel (especially Eloquent) is that using magic getters and setters can also cause unexpected situations. Such a &lt;a href="https://www.php.net/manual/en/language.oop5.overloading.php#object.get" rel="noopener noreferrer"&gt;magic getter&lt;/a&gt; is e.g. implemented in Laravel's &lt;a href="https://github.com/laravel/framework/blob/v11.38.2/src/Illuminate/Database/Eloquent/Model.php#L2258" rel="noopener noreferrer"&gt;&lt;code&gt;Model&lt;/code&gt; class&lt;/a&gt;, which calls the rather complicated &lt;a href="https://github.com/laravel/framework/blob/v11.38.2/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php#L464" rel="noopener noreferrer"&gt;&lt;code&gt;getAttribute&lt;/code&gt; method&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While this might make sense for an ORM to implement &lt;a href="https://en.wikipedia.org/wiki/Lazy_loading" rel="noopener noreferrer"&gt;lazy loading&lt;/a&gt; in a transparent way, it can still lead to wrong expectations, because accessing a property will not just read a value from a property. Have a look at the following example (using the &lt;code&gt;Flight&lt;/code&gt; example from the &lt;a href="https://laravel.com/docs/11.x/eloquent" rel="noopener noreferrer"&gt;Eloquent documentation&lt;/a&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="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$flight&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some developers might think it is safe to assume that this just reads the &lt;code&gt;name&lt;/code&gt; property of the &lt;code&gt;$flight&lt;/code&gt; object and outputs it. However, &lt;strong&gt;with the magic getter shown above, this code even might load something from the database, causing a potential performance bottleneck in a seemingly innocent operation&lt;/strong&gt;. Additionally, this is also another indirection (executing a function instead of just accessing a property), which needs to be understood by developers reading that code.&lt;/p&gt;

&lt;p&gt;Even though I am listing this here as kind of a negative example, I am still excited about the new &lt;a href="https://www.php.net/manual/en/language.oop5.property-hooks.php" rel="noopener noreferrer"&gt;property hooks feature in PHP&lt;/a&gt;. I am well aware of the fact that these property hooks allow to introduce the exact same problems, but there are still some valid use cases (like the lazy loading example mentioned above). In my opinion they could also be useful to allow for a consistent class-level API without forcing you to mix property accessors and getters.&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="kd"&gt;class&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;__construct&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;readonly&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&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="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$area&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;get&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;width&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;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;span class="nv"&gt;$rectangle&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="mi"&gt;10&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="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$rectangle&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="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$rectangle&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="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$rectangle&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;area&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By making use of property hooks we can avoid having a &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; property but a &lt;code&gt;getArea&lt;/code&gt; method. The alternative would be to make all three properties only accessible via a getter, but that would lead to lots of boilerplate code.&lt;/p&gt;

&lt;p&gt;However, I will try to restrict using these property hooks to only relatively simple operations (with some exceptions like lazy loading). This way the potential performance issues caused by seemingly innocent code may be mitigated.&lt;/p&gt;

&lt;h2&gt;
  
  
  State management libraries
&lt;/h2&gt;

&lt;p&gt;Something I have already seen in many different code bases using frontend libraries like &lt;a href="https://react.dev/" rel="noopener noreferrer"&gt;React&lt;/a&gt; and &lt;a href="https://vuejs.org/" rel="noopener noreferrer"&gt;Vue&lt;/a&gt; is that developers use advanced state management solutions (e.g. &lt;a href="https://redux.js.org/" rel="noopener noreferrer"&gt;Redux&lt;/a&gt;, &lt;a href="https://vuex.vuejs.org/" rel="noopener noreferrer"&gt;Vuex&lt;/a&gt;, or &lt;a href="https://pinia.vuejs.org/" rel="noopener noreferrer"&gt;Pinia&lt;/a&gt;) way too often.&lt;/p&gt;

&lt;p&gt;Let me explain that by an example I stumbled upon some time ago. In a Vue project there was a component responsible for displaying a share link. The link was displayed in a modal, which was shown after a button was clicked. It also included additional functionality like having another button copying that link to the clipboard, but for the sake of the example I am going to keep it minimal. I am also using Pinia instead of Vuex, and from my experience the Vuex impact would be even bigger.&lt;/p&gt;

&lt;p&gt;Although this is quite a simple component, it was implemented using a store. This resulted in a &lt;code&gt;ShareModel.vue&lt;/code&gt; file looking something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;useShareModalStore&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./stores/share-modal.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useShareModalStore&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dialog&lt;/span&gt; &lt;span class="na"&gt;:open=&lt;/span&gt;&lt;span class="s"&gt;"store.open"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;readonly&lt;/span&gt; &lt;span class="na"&gt;:value=&lt;/span&gt;&lt;span class="s"&gt;"store.url"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/input&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dialog&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, all this component does is putting a URL into a readonly input field within a dialog, and the dialog is only shown when the store's &lt;code&gt;open&lt;/code&gt; property is set to &lt;code&gt;true&lt;/code&gt;. While the above file looks pretty simple, we still need to implement the store returned by &lt;code&gt;useShareModalStore&lt;/code&gt; ourselves. This is usually done in a separate file &lt;code&gt;stores/share-modal.js&lt;/code&gt; that could be implement as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineStore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pinia&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useShareModalStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;share-modal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;open&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newUrl&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="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUrl&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;&lt;strong&gt;So just for storing a URL with a boolean flag indicating whether or not a modal should be shown we rely on another dependency (Pinia)&lt;/strong&gt;. The information is stored in a &lt;a href="https://vuejs.org/api/reactivity-core.html#ref" rel="noopener noreferrer"&gt;&lt;code&gt;ref&lt;/code&gt;&lt;/a&gt;, which is returned in a JavaScript object along with some methods that allow to manipulate it.&lt;/p&gt;

&lt;p&gt;Of course, we also need a third file, which acts as glue code. It makes use of the same store, sets the URL to be shared, and adds a button that calls the &lt;code&gt;show&lt;/code&gt; method when it is clicked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;useShareModalStore&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./stores/share-modal.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ShareModal&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./ShareModal.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useShareModalStore&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://www.danielrotter.at&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"store.show()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Share&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ShareModal&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is not only quite a lot of code for such a simple feature, it also comes with a serious limitation: &lt;strong&gt;This component cannot really be used multiple times on a page, at least not if you want to offer different share URLs.&lt;/strong&gt; The reason for this is that stores are global, and therefore changing the value of a store would affect all of the component instances. There is nothing wrong with this library, it is just not built for this use case, but that is a different story.&lt;/p&gt;

&lt;p&gt;So there is quite some added complexity, especially when comparing it to the simple Vue-only version. In that case the information is passed via props to the component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nf"&gt;defineProps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;open&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dialog&lt;/span&gt; &lt;span class="na"&gt;:open=&lt;/span&gt;&lt;span class="s"&gt;"open"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;readonly&lt;/span&gt; &lt;span class="na"&gt;:value=&lt;/span&gt;&lt;span class="s"&gt;"url"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/input&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dialog&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no need for a store at all, instead the information about the URL can be passed as a simple prop to the component, which also allows setting different URLs for each component instance. The &lt;code&gt;open&lt;/code&gt; state can be created as a &lt;code&gt;ref&lt;/code&gt; in the parent component, and also passed via a prop.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ShareModal&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./ShareModal.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;open&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"open = true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Share&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ShareModal&lt;/span&gt; &lt;span class="na"&gt;:open=&lt;/span&gt;&lt;span class="s"&gt;"open"&lt;/span&gt; &lt;span class="na"&gt;url=&lt;/span&gt;&lt;span class="s"&gt;"www.danielrotter.at"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is not only less code, there are also less dependencies (causing much less effort for updates), and the code is easier to understand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Command bus
&lt;/h2&gt;

&lt;p&gt;A few years ago the concept of command buses became popular in PHP. &lt;a href="https://matthiasnoback.nl/2015/01/a-wave-of-command-buses/" rel="noopener noreferrer"&gt;Matthias Noback wrote a nice introduction article&lt;/a&gt;, and for this article I am blatantly stealing his example.&lt;/p&gt;

&lt;p&gt;The basic idea of a command bus is that it handles commands, which are represented in very simple command classes like the following:&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SignUp&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$emailAddress&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$password&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="nv"&gt;$emailAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$password&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;emailAddress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$emailAddress&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;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$password&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;Then, instead of having some weird logic in the controller, we have a clear border between HTTP and our domain, since the &lt;strong&gt;controller's only task is to create an object of a simple command object and pass it to the command bus&lt;/strong&gt;, whose responsibility it is to execute this command:&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserController&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;signUpAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$command&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;SignUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;request&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;'emailAddress'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;request&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;'password'&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;commandBus&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$command&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;However, the command bus does not and cannot know how to handle a command class you have made up completely by yourself. Therefore there must be another component, which is a command handler. This class contains the code that is actually handling the command:&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SignUpHandler&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;UserRepository&lt;/span&gt; &lt;span class="nv"&gt;$userRepository&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;userRepository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$userRepository&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;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;signUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$command&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;emailAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$command&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;password&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;userRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&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;Of course there are good reasons to implement something like that. The code in the controller is very minimal, and implementing a console command that does something similar becomes almost trivial – the code is basically the same, the only change is that you do not get the data from a request but from some other kind of input.&lt;/p&gt;

&lt;p&gt;But as usual, this does not come without downsides. A very common problem is that you cannot have a proper return value from a command bus, since depending on the command that is being passed you might wish to return something different. Also, it comes with some additional overhead, which you usually feel when debugging your application. &lt;strong&gt;If you step into the call to the command bus, you might have to go a few more layers deep in order to get to the command handler you have implemented on your own.&lt;/strong&gt; This makes debugging harder, since depending on the behavior you want to inspect, you might end up in the same code location multiple times, which forces you to use something like conditional breakpoints. And this is not the only time this adds some complexity.&lt;/p&gt;

&lt;p&gt;Especially if you do not have a good reason to make use of a command bus (like e.g. &lt;a href="https://matthiasnoback.nl/2015/01/responsibilities-of-the-command-bus/" rel="noopener noreferrer"&gt;transaction handling&lt;/a&gt;), you could get some of the advantages with a much simpler architecture.&lt;/p&gt;

&lt;p&gt;Usually I still like to have command classes for every action the user can trigger. But instead of passing it to a generic command bus, I just pass it to a service, which is very boring (but in a good way!):&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserController&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;signUpAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$command&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;SignUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;request&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;'emailAddress'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;request&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;'password'&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;userService&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;signUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$command&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 then the &lt;code&gt;UserService&lt;/code&gt; acts as the handler of the command:&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserService&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;signUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;SignUp&lt;/span&gt; &lt;span class="nv"&gt;$command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;signUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$command&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;emailAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$command&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;password&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;userRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&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;This approach shares quite some advantages, but it is much simpler, since there is no generic command bus sitting in between. This code can easily be statically analyzed, does not need any external dependencies, and is much easier to debug.&lt;/p&gt;

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

&lt;p&gt;In general many indirections result in a worse debugability (I guess that is part of maintainability, but in my opinion that would warrant its own term), more code, and therefore more stuff that could go wrong, especially if external dependencies are used. &lt;strong&gt;Therefore I advice you to stop for a moment before you use another indirection, and think about if the additional complexity is actually worth it.&lt;/strong&gt; In my experience the answer to this question is way more often "no" than you might think.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>php</category>
      <category>javascript</category>
      <category>vue</category>
    </item>
    <item>
      <title>Writing high quality tests</title>
      <dc:creator>Daniel Rotter</dc:creator>
      <pubDate>Wed, 11 Dec 2024 08:31:33 +0000</pubDate>
      <link>https://dev.to/danrot90/writing-high-quality-tests-32f</link>
      <guid>https://dev.to/danrot90/writing-high-quality-tests-32f</guid>
      <description>&lt;p&gt;Unfortunately, tests still do not get the attention they would deserve in many organizations. Sometimes it feels like developers feel guilty if they are not writing any tests, and at the same time test code is often not properly reviewed. Instead, the only thing that is often checked in a review is if there are any tests, which is a shame, because &lt;strong&gt;just having tests is not good enough&lt;/strong&gt;. Actually, they should be of at least the same quality as all other code in a project, if not even of higher quality. Otherwise testing might indeed hold you back, since tests fail far too often, are hard to understand, or take way too long to run. I have already discussed some of these points in &lt;a href="///2023/09/22/avoid-mocking-repositories-by-using-in-memory-implementations.html"&gt;my blog post about using in-memory implementations instead of repository mocks&lt;/a&gt;. Now I want to discuss some other, more general, things I look out for when writing tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Minimalism is key
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://stackoverflow.com/help/minimal-reproducible-example" rel="noopener noreferrer"&gt;Stack Overflow asks you to add minimal, reproducible examples to questions&lt;/a&gt;, and in my opinion this is also very good advice for writing tests for the exact same reasons. Especially when reading a test months after you have written it, it is much easier to fully understand what is happening if less stuff is happening. So &lt;strong&gt;only write the code that is absolutely necessary for the test&lt;/strong&gt;, and resist the temptation to add more stuff just because it is easy to do so. But the test code must of course still be complete, i.e. a test should contain as many lines as necessary, but as few as possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Go for the 100% code coverage
&lt;/h2&gt;

&lt;p&gt;That might be an unpopular opinion, but I think it totally makes sense to aim for a 100% code coverage, even though many seem to consider this a bad practice.&lt;/p&gt;

&lt;p&gt;Sometimes teams settle for a lower value, e.g. a code coverage of 90%. However, that does not make a lot of sense to me. First of all, all these numbers are somewhat arbitrary and hard to back up using data. Also, when writing new code, not all of it needs to be tested in order to pass that threshold. And if somebody managed to get the coverage up the next person could get away with writing no tests at all while still keeping a code coverage higher than 90%, which results in a wrong feeling of confidence.&lt;/p&gt;

&lt;p&gt;One of the excuses I often hear is that it does not make sense to write tests for simple functions like getters and setters. And maybe surprisingly, I totally agree with that. But here is the catch: &lt;strong&gt;If none of the tests actually use these getters and setters, then there is probably no need to have them.&lt;/strong&gt; So instead of complaining about how hard it is to achieve 100% test coverage, it would most likely be better to not write code that is not required in the first place. This also avoids the maintenance burden every line of code brings with it.&lt;/p&gt;

&lt;p&gt;However, there is a small catch: Sometimes code does weird things, which might cause code coverage tools to mark some lines as uncovered, even though it was executed during the test run. I did not run into situations like this a lot, but if there is no way to make this work I exclude them from code coverage. E.g. PHPUnit allows to do that using their &lt;a href="https://docs.phpunit.de/en/10.5/code-coverage.html#ignoring-code-blocks" rel="noopener noreferrer"&gt;&lt;code&gt;codeCoverageIgnore&lt;/code&gt; annotation&lt;/a&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="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SomeClass&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/**
     * @codeCoverageIgnore
     */&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;doSomethingNotDetectedAsCovered&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;This way this function is not included in the code coverage analysis, &lt;strong&gt;which means it is still possible to reach a code coverage of 100%&lt;/strong&gt;, and I also keep checking for that value. The alternative is to settle for a lower value than 100%, but then there are the same issues mentioned above: Other code might also not be covered by tests, and that might be missed.&lt;/p&gt;

&lt;p&gt;With that being said, a 100% code coverage certainly does not give any guarantees that your code does not have any bugs. But if you do have uncovered lines in your application code you are not even giving your tests a change to spot potential errors in that line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Write good assertions
&lt;/h2&gt;

&lt;p&gt;The reason tests are being written is that we want to assert a certain behavior of the code. Therefore assertions are a very essential part of testing.&lt;/p&gt;

&lt;p&gt;Of course the most important consideration when writing assertions is that it correctly tests the code's behavior. But a very close second is how the assertion behaves when the code is failing. If an assertions fails for whatever reason, the problem should be as obvious to the developer as possible. A situation in which this is apparent is the situation that is currently being worked on in &lt;a href="https://github.com/symfony/symfony/pull/58456" rel="noopener noreferrer"&gt;this Symfony pull request&lt;/a&gt;. Symfony comes with a &lt;code&gt;assertResponseStatusCodeSame&lt;/code&gt; method, which allows to check for the status code of a response in a functional test:&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LoginControllerTest&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;WebTestCase&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;testFormAttributes&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;$client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'/login'&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;assertResponseStatusCodeSame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&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;assertSelectorCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'input[name="email"][required]'&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 problem with this test is the output it generates in case the status code is not &lt;code&gt;200&lt;/code&gt;. Since tests usually run in a development environment, Symfony will return an error page when this URL is accessed, and the &lt;code&gt;assertResponseStatusCodeSame&lt;/code&gt; method will output the entire response in case the assertion fails. This output is incredibly long, since this does not only return HTML, but also CSS and JavaScript, and my scrollback buffer is literally too small to allow me to read the entire message.&lt;/p&gt;

&lt;p&gt;This is absolutely the worst example I have encountered so far, but it can also be annoying if the wrong assertions are used in the code. Let us have a look at the output of the &lt;code&gt;assertSelectorCount&lt;/code&gt; assertion above, which fails with the following message if the given selector does not yield exactly one element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Failed asserting that the Crawler selector "input[name="email"][required]" was expected to be found 1 time(s) but was found 0 time(s).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It gives a pretty good idea about the occuring problem. However, the assertion could also be written in a different way (do not do this at home!):&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;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getCrawler&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;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'input[name="email"][required]'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Somebody might argue that this does exactly the same, therefore it does not matter which variant is used. This could not be further from the truth, since the following message appears if there is not a single required &lt;code&gt;input&lt;/code&gt; field for an email:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Failed asserting that false is true.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This does not help at all, and whoever works on fixing the problem first of all has to figure out what the problem actually is. What this shows, is that always a fitting assertion should be used, and &lt;a href="https://docs.phpunit.de/en/11.4/assertions.html" rel="noopener noreferrer"&gt;PHPUnit comes with many assertions&lt;/a&gt; fitting all kind of use cases. Sometimes it even makes sense to create a custom assertion.&lt;/p&gt;

&lt;p&gt;A relatively new assertion I have seen gaining popularity in recent years is &lt;a href="https://jestjs.io/docs/snapshot-testing" rel="noopener noreferrer"&gt;snapshot testing&lt;/a&gt;. Especially when starting to work on a front end project it seems to help a lot. I've often used it with React in the past. The main gist is that your tests look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;renderer&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-test-renderer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../Component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;renders correctly&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;renderer&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toJSON&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toMatchSnapshot&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 magic happens in the &lt;code&gt;toMatchSnapshot&lt;/code&gt; method. In the very first run it write the content of the &lt;code&gt;tree&lt;/code&gt; variable into a separate file. On subsequent runs it compares the new value of the &lt;code&gt;tree&lt;/code&gt; value with what it has previously stored in its separate file. If something changed it will fail the test and show a diff, with an option to update the snapshot again, which means you can fix your tests in a blink of an eye.&lt;/p&gt;

&lt;p&gt;While this sounds really nice, it also comes with some downsides. First, snapshots are quite brittle, because whenever the rendered markup of the &lt;code&gt;Component&lt;/code&gt; changes the tests will fail. Second, the intent of the test is hidden, since it does not explain what the author actually wanted to test.&lt;/p&gt;

&lt;p&gt;However, what I really enjoyed about it, was that whenever I changed a component, it reminded me of all other components using that component, because all those snapshots failed on the next run. For this reason I liked having at least one snapshot test per component.&lt;/p&gt;

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

&lt;p&gt;So to sum up, I think there are a few things you could start doing right away in order to improve the quality of your tests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep the code in a test to the absolutely required minimum&lt;/li&gt;
&lt;li&gt;Aim for a code coverage of a 100% and properly exclude code from the code coverage mechanism if it cannot be tested&lt;/li&gt;
&lt;li&gt;Use the correct assertions to get better error messages when your tests are failing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In my opinion following these few rules will already make a huge difference and help you enjoying working in the code base for a long time!&lt;/p&gt;

</description>
      <category>testing</category>
      <category>php</category>
      <category>javascript</category>
      <category>react</category>
    </item>
    <item>
      <title>Finding all HTML tags in a project not being self-closed</title>
      <dc:creator>Daniel Rotter</dc:creator>
      <pubDate>Tue, 14 May 2024 14:36:14 +0000</pubDate>
      <link>https://dev.to/danrot90/finding-all-html-tags-in-a-project-not-being-self-closed-22dc</link>
      <guid>https://dev.to/danrot90/finding-all-html-tags-in-a-project-not-being-self-closed-22dc</guid>
      <description>&lt;p&gt;I am currently working on upgrading an existing &lt;a href="https://vuejs.org/" rel="noopener noreferrer"&gt;Vue&lt;/a&gt; project from version 2 to 3, which involves&lt;br&gt;
&lt;a href="https://v3-migration.vuejs.org/breaking-changes/" rel="noopener noreferrer"&gt;quite some breaking changes&lt;/a&gt;. I don't want to go into the details,&lt;br&gt;
but at one point it was useful to find all elements of a certain Vue component that were not self-closed. In this&lt;br&gt;
specific, case it was about a &lt;code&gt;base-input&lt;/code&gt; component. The following cases were of interest to me:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;base-input&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"Some text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/base-input&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;base-input&lt;/span&gt; &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Some text in a slot&lt;span class="nt"&gt;&amp;lt;/base-input&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, the following were not:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;base-input&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"Some text"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;base-input&lt;/span&gt; &lt;span class="na"&gt;disabled&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There were quite some occurrences of this component in the entire project, therefore just searching for &lt;code&gt;base-input&lt;/code&gt; was&lt;br&gt;
not going to cut it for me. Instead, I decided to use regular expressions resp. regex with&lt;br&gt;
&lt;a href="https://github.com/BurntSushi/ripgrep" rel="noopener noreferrer"&gt;ripgrep&lt;/a&gt;. After installing ripgrep it provides a &lt;code&gt;rg&lt;/code&gt; command line tool.&lt;/p&gt;

&lt;p&gt;The following solution worked for my use case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rg &lt;span class="nt"&gt;--multiline&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;base-input[^&amp;gt;]*[^/]&amp;gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break it down:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;--multiline&lt;/code&gt; flag will make sure that this pattern is also matched across multiple lines, i.e. the match can
contain line breaks.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;&amp;lt;base-input&lt;/code&gt; will be searched for literally, i.e. this exact character sequence.&lt;/li&gt;
&lt;li&gt;With &lt;code&gt;[^&amp;gt;]*&lt;/code&gt; an arbitrary amount (that's what &lt;code&gt;*&lt;/code&gt; stands for) of characters not being &lt;code&gt;&amp;gt;&lt;/code&gt; will be matched.&lt;/li&gt;
&lt;li&gt;After that, there must be at least one character not being a &lt;code&gt;/&lt;/code&gt;, which would indicate a self-closing tag.&lt;/li&gt;
&lt;li&gt;Finally, the &lt;code&gt;&amp;gt;&lt;/code&gt; finishes the tag.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Although this works for the above examples, it is not a universal solution to the problem.&lt;/strong&gt; It does for instance not&lt;br&gt;
match the following cases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;base-input&amp;gt;&amp;lt;/base-input&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;base-input&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"Some &amp;gt; text"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first line will not be matched, because there must be at least one character not being &lt;code&gt;/&lt;/code&gt; after the &lt;code&gt;&amp;lt;base-input&lt;/code&gt;&lt;br&gt;
literal. Fortunately, that was not a problem for me, since I knew that using that component without attributes does not&lt;br&gt;
make any sense, so I could ignore that case.&lt;/p&gt;

&lt;p&gt;The second line will match although it shouldn't, since it recognized the &lt;code&gt;&amp;gt;&lt;/code&gt; within the quotes as the end of the tag.&lt;br&gt;
This will result in a false positive, but that was also fine for me since this did not occur quite often in the code&lt;br&gt;
base.&lt;/p&gt;

&lt;p&gt;Unfortunately, it is not even possible to write a full HTML parser using regular expressions, even though so many people&lt;br&gt;
ask about this on Stack Overflow that they've decided to make this part of their &lt;a href="https://stackoverflow.com/questions/22937618/reference-what-does-this-regex-mean/22944075#22944075" rel="noopener noreferrer"&gt;regular expressions&lt;br&gt;
FAQ&lt;/a&gt;. &lt;strong&gt;But that&lt;br&gt;
should not stop you from using regular expressions to do quick one-off tasks such as finding some occurrences in a big&lt;br&gt;
code base if you know the limitations and how they might affect the results.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>html</category>
      <category>regex</category>
      <category>vue</category>
      <category>cli</category>
    </item>
    <item>
      <title>git bisect and the importance of a clean history</title>
      <dc:creator>Daniel Rotter</dc:creator>
      <pubDate>Thu, 23 Nov 2023 20:52:03 +0000</pubDate>
      <link>https://dev.to/danrot90/git-bisect-and-the-importance-of-a-clean-history-30ki</link>
      <guid>https://dev.to/danrot90/git-bisect-and-the-importance-of-a-clean-history-30ki</guid>
      <description>&lt;p&gt;Most of the projects respectively teams I have seen so far do not seem to care too much about a clean git history.&lt;br&gt;
Therefore the git history of many projects look more like the following comic shows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fdfqjj6ov35qcknwzhsau.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fdfqjj6ov35qcknwzhsau.png" alt="A list of useless git commit messages" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course, &lt;a href="https://xkcd.com/1296/" rel="noopener noreferrer"&gt;xkcd&lt;/a&gt; might try to exaggerate a bit, &lt;strong&gt;but unfortunately, I have seen commit&lt;br&gt;
messages like those way too often&lt;/strong&gt;. This includes messages like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;adding stuff (what stuff? this could be about anything)&lt;/li&gt;
&lt;li&gt;fix bug (that does not even scratch the surface)&lt;/li&gt;
&lt;li&gt;iuwqruphsdauifj (aka just hitting the keyboard)&lt;/li&gt;
&lt;li&gt;fix CI (this one is often repeated in multiple consecutive commits and followed by one using swear words)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;A good commit message acts as documentation and can be immensely helpful when trying to figure something out later.&lt;/strong&gt;&lt;br&gt;
Very often only single-line commit messages are used (I am guilty of that myself), but even the &lt;a href="https://git-scm.com/docs/git-commit#_discussion" rel="noopener noreferrer"&gt;&lt;code&gt;git commit&lt;/code&gt;&lt;br&gt;
documentation page&lt;/a&gt; mentions that it is recommended to begin a commit&lt;br&gt;
using a short line acting as a heading, which can be followed by a thorough description. An example of a thorough&lt;br&gt;
description is &lt;a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=eb96e221937af3c7bb8a63208dbab813ca5d3d7e" rel="noopener noreferrer"&gt;this (at the time of writing) recent commit in the Linux&lt;br&gt;
kernel&lt;/a&gt;.&lt;br&gt;
Yes, almost everything until the start of the diff itself is the commit message, and while I am not that fluent with the&lt;br&gt;
development of an operating system, &lt;strong&gt;I am pretty sure that there is quite some interesting information in it, which&lt;br&gt;
might be useful when hunting down a bug or trying to better understand some decisions later&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But having a clean commit history does not stop at commit messages. Although this is already a big help, git can help a&lt;br&gt;
lot more when it is being mastered. Some features only work well if &lt;a href="https://martinfowler.com/articles/branching-patterns.html#healthy-branch" rel="noopener noreferrer"&gt;branches are kept&lt;br&gt;
healthy&lt;/a&gt;, i.e. &lt;strong&gt;every commit represents a&lt;br&gt;
working state&lt;/strong&gt;. So instead of just ending with saying that a clean history is important, I would like to promote an in&lt;br&gt;
my opinion way too unpopular feature of git.&lt;/p&gt;
&lt;h2&gt;
  
  
  Finding bugs with &lt;code&gt;git bisect&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Some time ago I prepared a small example in my &lt;a href="https://github.com/danrot/git-bisect-example" rel="noopener noreferrer"&gt;&lt;code&gt;git-bisect-example&lt;/code&gt;&lt;br&gt;
repository&lt;/a&gt;, which contains a small application written in&lt;br&gt;
&lt;a href="https://nodejs.org/en" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt;. This application is a very simple calculator, which has been implemented incrementally&lt;br&gt;
using multiple commits. Unfortunately, one of these commits contains an error, which leads the &lt;code&gt;+&lt;/code&gt; operator to subtract&lt;br&gt;
two numbers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ node index.js
First operator: 5
Operator: +
Second operator: 2
3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So everybody who knows basic arithmetic should see that there is something odd here. What seems even odder to the&lt;br&gt;
development team is that they are absolutely certain that this was working properly at some point. So after looking at&lt;br&gt;
the output of &lt;code&gt;git log&lt;/code&gt; they remember that the addition of two numbers was working properly after the commit&lt;br&gt;
&lt;code&gt;d3c66b49c330e58a70fe0abda56b691e1bb5db75&lt;/code&gt;. So they &lt;a href="https://git-scm.com/docs/git-switch/2.23.0" rel="noopener noreferrer"&gt;switch&lt;/a&gt; to this commit&lt;br&gt;
and execute the program again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git switch -d d3c66b49c330e58a70fe0abda56b691e1bb5db75
HEAD is now at d3c66b4 Refactor to use switch to differ between different operators

$ node index.js
First operator: 5
Operator: +
Second operator: 2
7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This shows that the development team was right about that, and &lt;strong&gt;the problem space has been reduced to a specific set of&lt;br&gt;
commits&lt;/strong&gt;. Now a developer can go through all of those commits, test again, and can find the (hopefully small) commit&lt;br&gt;
causing the issue. However, if the issues persisted for quite a long time there could be hundreds of commits in between,&lt;br&gt;
which still results in a very high effort.&lt;/p&gt;

&lt;p&gt;Luckily, git comes with an awesome command called &lt;code&gt;git bisect&lt;/code&gt;. &lt;code&gt;bisect&lt;/code&gt; stands for "binary search commit", and this is&lt;br&gt;
exactly what it does. It performs a &lt;a href="https://en.wikipedia.org/wiki/Binary_search_algorithm" rel="noopener noreferrer"&gt;binary search&lt;/a&gt; using the git&lt;br&gt;
history, which leads to much less effort when looking for a specific commit. Let's see how that works in practice.&lt;/p&gt;

&lt;p&gt;First of all the binary search is started using &lt;code&gt;git bisect start&lt;/code&gt; followed by a &lt;code&gt;git bisect good&lt;/code&gt; indicating that the&lt;br&gt;
currently checked out commit is not working.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git bisect start
status: waiting for both good and bad commits

$ git bisect good
status: waiting for bad commit, 1 good commit known
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Afterwards, &lt;code&gt;git switch -&lt;/code&gt; is used to checkout the previously checked out commit, which was failing before. Therefore we&lt;br&gt;
can mark this commit as working using &lt;code&gt;git bisect bad&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git switch -
warning: you are switching branch while bisecting
Previous HEAD position was d3c66b4 Refactor to use switch to differ between different operators
Switched to branch 'master'
Your branch is up to date with 'origin/master'.

$ git bisect bad
Bisecting: 2 revisions left to test after this (roughly 1 step)
[3eaa08756c7ae6273effa173a3aba7d1fe4a8929] Added support for multiplication
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By marking one commit as bad and one as good we have defined the range in which the error must have been introduced.&lt;br&gt;
After doing so, git has checked out the commit in the middle of the range. Now we can do the same test again, realize&lt;br&gt;
that the error does not happen in this commit, and mark it as a good one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ node index.js
First operator: 5
Operator: +
Second operator: 2
7

$ git bisect good
Bisecting: 0 revisions left to test after this (roughly 1 step)
[124439abe0fb289109f915dcdd07f1ecb0041f79] Add support for subtraction
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;By doing so half of the commits have already been eliminated as the cause of the bug.&lt;/strong&gt; Since the older commit used to&lt;br&gt;
work and the commit in the middle of the range is still working, the error must be introduced later. Git knows that now&lt;br&gt;
and automatically checked out the next commit, which is in the middle of the even narrower range left.&lt;/p&gt;

&lt;p&gt;So let's test again and type &lt;code&gt;git bisect bad&lt;/code&gt; afterwards since we will see that the error occurs now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ node index.js
First operator: 5
Operator: +
Second operator: 2
3

$ git bisect bad
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[c4dc15f032ca15dcc84cd877a0babf4b0dfc1e8d] Add support for division
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, the commits in question can be put in half, and git checks out the next commit for us to check. This one is&lt;br&gt;
working again, so we will mark it as a good commit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ node index.js
First operator: 5
Operator: +
Second operator: 3
8

$ git bisect good
124439abe0fb289109f915dcdd07f1ecb0041f79 is the first bad commit
commit 124439abe0fb289109f915dcdd07f1ecb0041f79
Author: Daniel Rotter &amp;lt;daniel.rotter@gmail.com&amp;gt;
Date:   Sat Feb 19 11:12:05 2022 +0100

    Add support for subtraction

 index.js | 2 ++
 1 file changed, 2 insertions(+)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now git can already tell us the exact commit that caused the error. We can have a closer look by using the &lt;code&gt;git show&lt;/code&gt;&lt;br&gt;
command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git show 124439abe0fb289109f915dcdd07f1ecb0041f79
commit 124439abe0fb289109f915dcdd07f1ecb0041f79
Author: Daniel Rotter &amp;lt;daniel.rotter@gmail.com&amp;gt;
Date:   Sat Feb 19 11:12:05 2022 +0100

    Add support for subtraction

diff --git a/index.js b/index.js
index 7800bbe..6a83dd9 100644
--- a/index.js
+++ b/index.js
@@ -9,6 +9,8 @@ let result;
 switch (operator) {
        case "+":
                result = operand1 + operand2;
+       case "-":
+               result = operand1 - operand2;
                break;
        case "*":
                result = operand1 * operand2;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Knowing that these two added lines are causing the error makes bug hunting a lot easier.&lt;/strong&gt; In this example, the reason&lt;br&gt;
was a missing &lt;code&gt;break&lt;/code&gt; statement.&lt;/p&gt;

&lt;p&gt;Now this was a rather small example, but the interesting part is that &lt;a href="https://en.wikipedia.org/wiki/Binary_search_algorithm" rel="noopener noreferrer"&gt;binary&lt;br&gt;
search&lt;/a&gt; has a logarithmic complexity, i.e. the savings get much&lt;br&gt;
bigger with an increasing number of commits, e.g. &lt;strong&gt;if the specified range contains 100 commits roughly 7 steps will be&lt;br&gt;
necessary to find the commit in question&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This process can even be further automated if there is a script that can tell if the code contains the error. In that&lt;br&gt;
case the &lt;a href="https://git-scm.com/docs/git-bisect#_bisect_run" rel="noopener noreferrer"&gt;&lt;code&gt;git bisect run&lt;/code&gt; command&lt;/a&gt; can be used, which will spare&lt;br&gt;
developers from typing &lt;code&gt;git bisect bad&lt;/code&gt; and &lt;code&gt;git bisect good&lt;/code&gt; after testing for the defect manually.&lt;/p&gt;
&lt;h2&gt;
  
  
  The importance of a clean history
&lt;/h2&gt;

&lt;p&gt;But now the catch: &lt;strong&gt;All of this can only work with a clean git history containing only working commits.&lt;/strong&gt; Imagine that&lt;br&gt;
the development team is not so strict about branches being healthy. This might lead to situations in which already the&lt;br&gt;
bootstrapping of the application and therefore every program execution fails. In that case, &lt;strong&gt;the &lt;code&gt;git bisect&lt;/code&gt; command&lt;br&gt;
is rendered useless since developers using it cannot tell if the error they are looking for appears in the current&lt;br&gt;
commit or if the program already failed before the error could appear&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So having a clean and healthy branch is not just an academic exercise, if features like &lt;code&gt;git bisect&lt;/code&gt; should be used&lt;br&gt;
having a clean history is non-optional.&lt;/strong&gt; And it would really be a pity to not be able to use one of git's most awesome&lt;br&gt;
features. This is one of the reasons I consider &lt;a href="https://kellysutton.com/2019/07/15/keep-all-commits-green.html" rel="noopener noreferrer"&gt;keeping all commits&lt;br&gt;
green&lt;/a&gt; a best practice.&lt;/p&gt;
&lt;h2&gt;
  
  
  Keeping a clean history
&lt;/h2&gt;

&lt;p&gt;As mentioned previously, it is not that easy to convince an entire team to only create green, i.e. working, commits.&lt;br&gt;
&lt;strong&gt;Especially if there is a workflow using pull requests in place it might also take quite some effort to check if all&lt;br&gt;
commits within this pull request are working.&lt;/strong&gt; The problem is that the code hosting platforms I know only check the&lt;br&gt;
latest commit when a branch is pushed, i.e. if a developer commits multiple times locally and pushes all those commits&lt;br&gt;
at once the only thing a reviewer can say for sure is that the state from the last commit does not fail any pipeline (if&lt;br&gt;
one is setup).&lt;/p&gt;

&lt;p&gt;I have seen quite a lot of arguing about this, &lt;strong&gt;but what works pretty well in my experience is to squash commits when a&lt;br&gt;
pull request is merged&lt;/strong&gt;. Squashing means that instead of keeping all commits in the history they will be combined into&lt;br&gt;
one new single commit.&lt;/p&gt;

&lt;p&gt;Imagine a git history that generates the following output when using &lt;code&gt;git log --graph --oneline&lt;/code&gt; (i.e. showing the graph&lt;br&gt;
on the left and compress commits to a single line):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git log --graph --oneline
* 8836470 (HEAD -&amp;gt; feature) Commit 6
* cec57f4 Commit 5
* c555d59 Commit 4
* e715723 Commit 3
* 55bc924 Commit 2
* 719d88d Commit 1
* 9ce9b8c (main) Initialize repository
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So there is currently a branch called &lt;code&gt;feature&lt;/code&gt; checked out, which adds some more commits on top of the &lt;code&gt;main&lt;/code&gt; branch.&lt;br&gt;
When we execute a &lt;code&gt;git merge&lt;/code&gt; with the &lt;code&gt;--no-ff&lt;/code&gt; option it will generate a new merge commit, which has two parent&lt;br&gt;
commits and all currently existing commits continue to do so.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git switch main
Switched to branch 'main'

$ git merge --no-ff
Merge made by the 'ort' strategy.
 README.md | 2 ++
 1 file changed, 2 insertions(+)

$ git log --graph --oneline
*   8869431 (HEAD -&amp;gt; main) Merge branch 'feature'
|\
| * 8836470 (feature) Commit 6
| * cec57f4 Commit 5
| * c555d59 Commit 4
| * e715723 Commit 3
| * 55bc924 Commit 2
| * 719d88d Commit 1
|/
* 9ce9b8c Initialize repository
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the graph on the left shows two different paths that will be combined in the merge commit. &lt;strong&gt;Seeing the entire&lt;br&gt;
history with all commits can also be valuable, but only if all commits are properly working.&lt;/strong&gt; If those commits are not&lt;br&gt;
working and/or contain commit messages like shown in the introduction of this blog post they will cause more harm than&lt;br&gt;
good by bloating the git history for no good reason. This complicates everything, especially the &lt;code&gt;git bisect&lt;/code&gt; command&lt;br&gt;
shown previously.&lt;/p&gt;

&lt;p&gt;Squashing commits can be done by using the &lt;code&gt;--squash&lt;/code&gt; option of the &lt;code&gt;git merge&lt;/code&gt; command. Then the changes from all&lt;br&gt;
commits will be added to the staging area, from where they can be committed as usual.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git merge --squash feature
Updating 9ce9b8c..8836470
Fast-forward
Squash commit -- not updating HEAD
 README.md | 2 ++
 1 file changed, 2 insertions(+)

$ git commit -m "Feature"
[main a0ebc67] Feature
 1 file changed, 2 insertions(+)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this procedure, the history looks completely different, although the end result is the same. All commits from the&lt;br&gt;
&lt;code&gt;feature&lt;/code&gt; branch have not landed in the &lt;code&gt;main&lt;/code&gt; branch, instead, all changes are squashed into a single commit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git log --graph --oneline
* a0ebc67 (HEAD -&amp;gt; main) Feature
* 9ce9b8c Initialize repository
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;This leads to a linear history and if pull requests are properly reviewed to working commits and therefore to a clean&lt;br&gt;
history.&lt;/strong&gt; Teams working with pull requests usually do such pull request reviews, sometimes even with manual testing,&lt;br&gt;
which should avoid having commits that do not work at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So by always squashing commits when merging pull requests, it is much easier to keep a clean history with only green&lt;br&gt;
commits.&lt;/strong&gt; This also ensures that features like &lt;code&gt;git bisect&lt;/code&gt; can do their work properly. &lt;strong&gt;The only downside with&lt;br&gt;
regards to &lt;code&gt;git bisect&lt;/code&gt; I can think of is that it yields bigger commits when pull requests are squashed&lt;/strong&gt;, which makes&lt;br&gt;
hunting down the error harder since the commit might return hundreds of lines, and not just a few. However, this is&lt;br&gt;
still better than not being able to use the feature at all, because some developers commit code not working properly.&lt;br&gt;
Luckily both&lt;br&gt;
&lt;a href="https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/configuring-commit-squashing-for-pull-requests" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;br&gt;
and &lt;a href="https://bitbucket.org/blog/git-squash-commits-merging-bitbucket" rel="noopener noreferrer"&gt;Bitbucket&lt;/a&gt; support squashing commits when merging&lt;br&gt;
pull requests.&lt;/p&gt;

&lt;p&gt;A completely different approach would be to use &lt;a href="https://martinfowler.com/articles/branching-patterns.html#continuous-integration" rel="noopener noreferrer"&gt;continuous&lt;br&gt;
integration&lt;/a&gt;, and by that I do not&lt;br&gt;
mean the pipeline running tests, but the process of directly committing to the &lt;code&gt;main&lt;/code&gt; branch. This is often combined&lt;br&gt;
with automatic tests, which also helps with keeping a clean history. However, that is a completely different workflow&lt;br&gt;
with other trade-offs. &lt;strong&gt;My only recommendation is to try keeping the git history as clean as possible.&lt;/strong&gt; How that is&lt;br&gt;
done exactly should probably be decided within the team.&lt;/p&gt;

</description>
      <category>git</category>
      <category>cli</category>
    </item>
    <item>
      <title>Avoid mocking repositories by using in-memory implementations</title>
      <dc:creator>Daniel Rotter</dc:creator>
      <pubDate>Tue, 10 Oct 2023 06:46:53 +0000</pubDate>
      <link>https://dev.to/danrot90/avoid-mocking-repositories-by-using-in-memory-implementations-1d0a</link>
      <guid>https://dev.to/danrot90/avoid-mocking-repositories-by-using-in-memory-implementations-1d0a</guid>
      <description>&lt;p&gt;One of the most important aspects of testing - besides finding errors in an application - is how long it takes to run&lt;br&gt;
them. If tests for an application take minutes or even hours to finish, then they are not suitable for developing using&lt;br&gt;
a fast feedback loop and developers might not run them as often as they should.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://martinfowler.com/bliki/TestPyramid.html" rel="noopener noreferrer"&gt;testing pyramid&lt;/a&gt; has many goals, and one of them is to have a fast&lt;br&gt;
test suite so that developers do not have to wait too long for their tests to finish. It does so by introducing three&lt;br&gt;
different kinds of tests: UI, service, and unit. &lt;strong&gt;The basic idea is that unit tests are the fastest to run, and&lt;br&gt;
therefore most of the testing should be implemented as unit tests.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Testing does not come with clear definitions for all of its terms, so I want to clarify that lately, I like to use&lt;br&gt;
&lt;a href="https://www.martinfowler.com/bliki/UnitTest.html" rel="noopener noreferrer"&gt;sociable unit tests over solitary ones&lt;/a&gt;. They make me much more&lt;br&gt;
confident since a real implementation is used for the dependencies of a unit. However, &lt;strong&gt;if not used carefully they&lt;br&gt;
might be very slow&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Solitary unit tests will always mock dependencies, which makes them fast &lt;strong&gt;since all dependencies of a unit are&lt;br&gt;
replaced with a mock implementation&lt;/strong&gt;. Very often some kind of library or framework is used for that, e.g. &lt;a href="https://docs.phpunit.de/en/9.6/test-doubles.html" rel="noopener noreferrer"&gt;test doubles&lt;br&gt;
from PHPUnit&lt;/a&gt; or a separate mocking library like&lt;br&gt;
&lt;a href="https://github.com/phpspec/prophecy" rel="noopener noreferrer"&gt;Prophecy&lt;/a&gt; or &lt;a href="https://github.com/mockery/mockery" rel="noopener noreferrer"&gt;Mockery&lt;/a&gt;. While they can make&lt;br&gt;
tests fast by setting up expectations and the desired return value, especially if used for slow parts like code&lt;br&gt;
connecting to a database, they come with some serious issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mocks can easily hide actual errors because they are still returning "old" values if the behavior of an
implementation changes for some reason.&lt;/li&gt;
&lt;li&gt;Mocks are often defined in multiple tests in a similar way, making them awkward to use compared to a "real"
implementation.&lt;/li&gt;
&lt;li&gt;Mocks are tightly coupled to the real implementation making refactorings even harder since a change might cause
necessary changes in many tests as well.&lt;/li&gt;
&lt;li&gt;Mocks are not very straightforward to define and make the test code harder to read, although that might be subjective.&lt;/li&gt;
&lt;li&gt;Mocking libraries often use dynamic classes, which makes understanding and debugging them quite hard. When stepping
into a mocked function call in a debug session there is no straightforward code. Instead, you might land in a &lt;a href="https://github.com/phpspec/prophecy/blob/098f8850e4bd800b7734c65b2c8c10b28d87f10e/src/Prophecy/Prophecy/MethodProphecy.php" rel="noopener noreferrer"&gt;file with
hundreds of lines of non-trivial
code&lt;/a&gt;,
or - even worse - in a dynamically created file not even existing in the file system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the beginning of my career, I was not aware of these issues and used solitary unit tests with loads of mocks. We&lt;br&gt;
often did refactorings, which did not make tests fail although the code was not working in production and I have spent&lt;br&gt;
quite some hours debugging third-party code.&lt;/p&gt;

&lt;p&gt;Fortunately, there is another method of making tests fast and have more reliable tests at the same time: &lt;strong&gt;Define a&lt;br&gt;
single interface, write an abstract test against that interface, and have the same tests run against one implementation&lt;br&gt;
for production and a much faster implementation for tests.&lt;/strong&gt; This will solve multiple of the issues above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Errors are less likely to be hidden because both implementations should behave the same since they run against the
same tests.&lt;/li&gt;
&lt;li&gt;The test implementation can be reused in every test instead of setting up mocks every time.&lt;/li&gt;
&lt;li&gt;The tests are using a simple class instead of a complex mocking library.&lt;/li&gt;
&lt;li&gt;Debuggers will land in a real class developers know, instead of something dynamically generated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rest of the blog post will explain how this can be done in Symfony, but the general principles should apply to any&lt;br&gt;
framework and programming language. &lt;strong&gt;The example code can also be found as a working application in a &lt;a href="https://github.com/danrot/memory-repository-testing" rel="noopener noreferrer"&gt;GitHub&lt;br&gt;
repository&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Define a common interface
&lt;/h2&gt;

&lt;p&gt;The example will implement two different repositories, one using the &lt;a href="https://www.doctrine-project.org/projects/orm.html" rel="noopener noreferrer"&gt;Doctrine&lt;br&gt;
ORM&lt;/a&gt; for use in production and an in-memory implementation using an&lt;br&gt;
array to store objects. I will use a generic &lt;code&gt;Item&lt;/code&gt; class to keep things generic:&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="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Domain&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;Doctrine\ORM\Mapping\Column&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;Doctrine\ORM\Mapping\Entity&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;Doctrine\ORM\Mapping\Id&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;Symfony\Component\Uid\Uuid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="na"&gt;#[Entity]&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Item&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;#[Id]&lt;/span&gt;
    &lt;span class="na"&gt;#[Column(type: 'uuid')]&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;Uuid&lt;/span&gt; &lt;span class="nv"&gt;$id&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="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;Column&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="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;Column&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;$description&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="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Uuid&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;v4&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;getId&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;Uuid&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;id&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;getTitle&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;$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="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;getDescription&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;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;description&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;&lt;em&gt;Good domain objects would contain more methods than just getters, but for the sake of brevity, I will keep it like that&lt;br&gt;
for this blog post.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is more or less the simplest Doctrine entity that can be created, it only contains a &lt;code&gt;Uuid&lt;/code&gt; as an identifier and a&lt;br&gt;
field for a title and a description. Additionally, the domain layer introduces an interface for an &lt;code&gt;ItemRepository&lt;/code&gt;,&lt;br&gt;
which takes care of persisting and retrieving objects from data storage:&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="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Domain&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ItemRepositoryInterface&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;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Item&lt;/span&gt; &lt;span class="nv"&gt;$item&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;/**
     * @return Item[]
     */&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;loadAll&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="cd"&gt;/**
     * @return Item[]
     */&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;loadFilteredByTitle&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;$titleFilter&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="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The contract defined in this interface allows the application to not care about which kind of storage is used, and&lt;br&gt;
therefore most tests can use a much faster one than a relational database.&lt;/strong&gt; However, in order to swap out&lt;br&gt;
implementations reliably it must be ensured that all of them behave in the same way. That is where the abstract test&lt;br&gt;
case comes in.&lt;/p&gt;
&lt;h2&gt;
  
  
  Implement the abstract test case
&lt;/h2&gt;

&lt;p&gt;As mentioned previously, the abstract test class is responsible for ensuring that all implementations of the&lt;br&gt;
&lt;code&gt;ItemRepositoryInterface&lt;/code&gt; behave in the same way. One characteristic of repositories is that adding the same object&lt;br&gt;
twice will result in having the object only once in the repository. So let's test that and adding two different objects&lt;br&gt;
to the repository as well as filtering items by their title. Since currently the &lt;code&gt;ItemRepository&lt;/code&gt; interface only has&lt;br&gt;
three methods this covers all of its functionality already.&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="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Tests\Repository&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;App\Domain\Item&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;App\Domain\ItemRepositoryInterface&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;Symfony\Bundle\FrameworkBundle\Test\KernelTestCase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AbstractItemRepositoryTest&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;KernelTestCase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;createItemRepository&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;ItemRepositoryInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;flush&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;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testMultipleAddOfItem&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;$itemRepository&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;createItemRepository&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nv"&gt;$item&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;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Test title'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Test description'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$itemRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$itemRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item&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="nb"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nv"&gt;$items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$itemRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;loadAll&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;assertCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$items&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;assertContains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$items&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;testLoadAllWithMultipleItems&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;$itemRepository&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;createItemRepository&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nv"&gt;$item1&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;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Test title 1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Test description 1'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$item2&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;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Test title 2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Test description 2'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$itemRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$itemRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item2&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="nb"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nv"&gt;$items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$itemRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;loadAll&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;assertCount&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;$items&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;assertContains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$items&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;assertContains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$items&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;testLoadFilteredByTitle&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;$itemRepository&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;createItemRepository&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nv"&gt;$item1&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;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Test title 1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Test description 1'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$item2&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;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Title 2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Description 2'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$item3&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;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Test title 3'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Test description 2'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$itemRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$itemRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$itemRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item3&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="nb"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nv"&gt;$items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$itemRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;loadFilteredByTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Test title'&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;assertCount&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;$items&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;assertContains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$items&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;assertContains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$items&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 test class needs to extend from the &lt;code&gt;KernelTestCase&lt;/code&gt; of Symfony to allow getting a reference to the&lt;br&gt;
&lt;code&gt;EntityManagerInterface&lt;/code&gt; of Doctrine, which enables testing against the real database for the Doctrine repository later.&lt;/p&gt;

&lt;p&gt;Also, two abstract methods need to be overridden by the tests for the concrete applications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;createItemRepository&lt;/code&gt; is a template method allowing to swap out the implementation for the tests.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;flush&lt;/code&gt; is used to actually send changes to the database, which is necessary for the Doctrine repository later, unless
you want to add the &lt;code&gt;flush&lt;/code&gt; call to the repository itself (which I would not recommend, since a single request should
have all of its changes or none being committed to the database).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With that abstract test case in place, the concrete implementations can be implemented and tested against the same set&lt;br&gt;
of tests.&lt;/p&gt;
&lt;h2&gt;
  
  
  Write the production and testing implementation
&lt;/h2&gt;

&lt;p&gt;The concrete implementations of these tests will override the &lt;code&gt;createMatchRequest&lt;/code&gt; and &lt;code&gt;flush&lt;/code&gt; methods. Therefore the&lt;br&gt;
test for the Doctrine implementation 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="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Tests\Repository\Doctrine&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;App\Domain\ItemRepositoryInterface&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;App\Repository\Doctrine\ItemRepository&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;App\Tests\Repository\AbstractItemRepositoryTest&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;Doctrine\ORM\EntityManagerInterface&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;ItemRepositoryTest&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;AbstractItemRepositoryTest&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;createItemRepository&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;ItemRepositoryInterface&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;ItemRepository&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;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="nc"&gt;EntityManagerInterface&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="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;flush&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;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="nc"&gt;EntityManagerInterface&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;setUp&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;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="nc"&gt;EntityManagerInterface&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getConnection&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;setNestTransactionsWithSavepoints&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="nv"&gt;$this&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="nc"&gt;EntityManagerInterface&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getConnection&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;beginTransaction&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;tearDown&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;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="nc"&gt;EntityManagerInterface&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getConnection&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;rollBack&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 here the &lt;code&gt;createItemRepository&lt;/code&gt; will return an instance of &lt;code&gt;App\Repository\Doctrine\ItemRepository&lt;/code&gt;, which also&lt;br&gt;
requires an instance of the &lt;code&gt;EntityManagerInterface&lt;/code&gt; to work properly since it uses this class to store and retrieve&lt;br&gt;
data from the database. The &lt;code&gt;flush&lt;/code&gt; method will call &lt;code&gt;flush&lt;/code&gt; on the &lt;code&gt;EntityManagerInterface&lt;/code&gt;, which results in the data&lt;br&gt;
actually being stored (this is called in the abstract test case). Additionally, the &lt;code&gt;setUp&lt;/code&gt; and &lt;code&gt;tearDown&lt;/code&gt; methods will&lt;br&gt;
ensure that each test is enclosed in a transaction by calling &lt;code&gt;beginTransaction&lt;/code&gt; and &lt;code&gt;rollBack&lt;/code&gt;. &lt;strong&gt;This way no data is&lt;br&gt;
actually stored in the database, which makes the tests very fast&lt;/strong&gt;. However, be careful, since there might still be&lt;br&gt;
database checks that could fail at this point. Last but not least the &lt;code&gt;setNestTransactionWithSavepoints&lt;/code&gt; method is&lt;br&gt;
necessary to allow nesting transactions.&lt;/p&gt;

&lt;p&gt;The following &lt;code&gt;ItemRepository&lt;/code&gt; implementation will make use of the &lt;code&gt;EntityManagerInterface&lt;/code&gt; and fulfill the previously&lt;br&gt;
shown tests:&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="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Repository\Doctrine&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;App\Domain\Item&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;App\Domain\ItemRepositoryInterface&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;Doctrine\ORM\EntityManagerInterface&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;ItemRepository&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;ItemRepositoryInterface&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="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;EntityManagerInterface&lt;/span&gt; &lt;span class="nv"&gt;$entityManager&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;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Item&lt;/span&gt; &lt;span class="nv"&gt;$item&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;entityManager&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;persist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item&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;loadAll&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="cd"&gt;/** @var Item[] */&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;entityManager&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;createQueryBuilder&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;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Item&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="s1"&gt;'i'&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;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'i'&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;getQuery&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;getResult&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;loadFilteredByTitle&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;$titleFilter&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="cd"&gt;/** @var Item[] */&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;entityManager&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;createQueryBuilder&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;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Item&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="s1"&gt;'i'&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;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'i'&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;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'i.title LIKE :titleFilter'&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;setParameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'titleFilter'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$titleFilter&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'%'&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;getQuery&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;getResult&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 tests for the memory implementation are a bit simpler since there is no dependency like the &lt;code&gt;EntityManagerInterface&lt;/code&gt;&lt;br&gt;
and there is also no need to call a method like &lt;code&gt;flush&lt;/code&gt;. Therefore &lt;code&gt;createItemRepository&lt;/code&gt; will just return a new&lt;br&gt;
instance and the &lt;code&gt;flush&lt;/code&gt; method can be left empty:&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="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Tests\Repository\Memory&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;App\Domain\ItemRepositoryInterface&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;App\Repository\Memory\ItemRepository&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;App\Tests\Repository\AbstractItemRepositoryTest&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;ItemRepositoryTest&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;AbstractItemRepositoryTest&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;createItemRepository&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;ItemRepositoryInterface&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;ItemRepository&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;flush&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The implementation fulfilling these tests uses a simple array containing the objects, which only needs to check if the&lt;br&gt;
array already contains the passed &lt;code&gt;Item&lt;/code&gt; to avoid inserting it multiple times:&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="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Repository\Memory&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;App\Domain\Item&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;App\Domain\ItemRepositoryInterface&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;ItemRepository&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;ItemRepositoryInterface&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/**
     * @var Item[]
     */&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$items&lt;/span&gt; &lt;span class="o"&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;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Item&lt;/span&gt; &lt;span class="nv"&gt;$item&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="nb"&gt;in_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item&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;items&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="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$item&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;loadAll&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="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;items&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;loadFilteredByTitle&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;$titleFilter&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="nb"&gt;array_values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nb"&gt;array_filter&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;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Item&lt;/span&gt; &lt;span class="nv"&gt;$item&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;str_contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getTitle&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nv"&gt;$titleFilter&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The only bit that is a bit cumbersome here is the &lt;code&gt;loadFilteredByTitle&lt;/code&gt; method, since this method will only be&lt;br&gt;
implemented for the tests, which would not be necessary if mocks were used.&lt;/strong&gt; But therefore mocks might lead to wrong&lt;br&gt;
test results if the behavior of this method changes for some reason. In this example &lt;code&gt;array_filter&lt;/code&gt; was used to return&lt;br&gt;
only the items matching the given criteria, but it would also be possible to use a &lt;code&gt;foreach&lt;/code&gt; loop or whatever else works&lt;br&gt;
for you. Of course this is still a very simple example and depending on the actual logic this might be harder to&lt;br&gt;
implement, but I would not consider this wasted effort since it gives me confidence and fast tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This implementation cannot be used in a production environment unless you want every request to start with no data at&lt;br&gt;
all.&lt;/strong&gt; However, other than that, this implementation behaves exactly the same as Doctrine one actually storing data in&lt;br&gt;
the database. &lt;strong&gt;This makes it a great candidate to use for other tests, many of which mocks would probably be used&lt;br&gt;
otherwise.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Use the correct implementation in each environment
&lt;/h2&gt;

&lt;p&gt;So now that we have two implementations of the same interface we can use them interchangeably in e.g. a REST Controller&lt;br&gt;
like shown in the following code:&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="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Controller&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;App\Domain\Item&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;App\Domain\ItemRepositoryInterface&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;Symfony\Bundle\FrameworkBundle\Controller\AbstractController&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;Symfony\Component\HttpFoundation\JsonResponse&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;Symfony\Component\HttpFoundation\Request&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;Symfony\Component\Routing\Annotation\Route&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;ItemController&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;AbstractController&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;#[Route('/items', methods: ['GET'])]&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;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;ItemRepositoryInterface&lt;/span&gt; &lt;span class="nv"&gt;$itemRepository&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;JsonResponse&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$titleFilter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'titleFilter'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$titleFilter&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nv"&gt;$itemRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;loadFilteredByTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$titleFilter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$itemRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;loadAll&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;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$items&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="na"&gt;#[Route('/items', methods: ['POST'])]&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;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;ItemRepositoryInterface&lt;/span&gt; &lt;span class="nv"&gt;$itemRepository&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;JsonResponse&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cd"&gt;/** @var \stdClass */&lt;/span&gt;
        &lt;span class="nv"&gt;$data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getContent&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="nv"&gt;$item&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;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$data&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="nv"&gt;$data&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$itemRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item&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;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item&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;This is a pretty standard Symfony controller using the &lt;code&gt;ItemRepositoryInterface&lt;/code&gt; to inject one of the above&lt;br&gt;
implementations. Symfony comes with &lt;a href="https://symfony.com/doc/current/service_container/autowiring.html" rel="noopener noreferrer"&gt;autowiring&lt;/a&gt;&lt;br&gt;
these days so that usually it is not necessary to configure anything. However, since we have two implementations of the&lt;br&gt;
&lt;code&gt;ItemRepositoryInterface&lt;/code&gt; Symfony cannot know which one to use. Therefore we have to add the following line to the&lt;br&gt;
&lt;code&gt;config/services.yaml&lt;/code&gt; file:&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;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# other stuff...&lt;/span&gt;
    &lt;span class="na"&gt;App\Domain\ItemRepositoryInterface&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@App\Repository\Doctrine\ItemRepository'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way Symfony knows that it should inject the Doctrine &lt;code&gt;ItemRepository&lt;/code&gt; whenever the &lt;code&gt;ItemRepositoryInterface&lt;/code&gt; is&lt;br&gt;
used.&lt;/p&gt;

&lt;p&gt;Mind that the controller does not call the &lt;code&gt;EntityManagerInterface::flush&lt;/code&gt; method. I like to avoid using such methods in&lt;br&gt;
the controller, since depending on which &lt;code&gt;ItemRepositoryInterface&lt;/code&gt; is being used it might not be necessary. However, in&lt;br&gt;
the case of the Doctrine implementation this must be done, therefore I started to implement a listener for that:&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="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Repository\Doctrine&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;Doctrine\ORM\EntityManagerInterface&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;Symfony\Component\EventDispatcher\EventSubscriberInterface&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;Symfony\Component\HttpKernel\KernelEvents&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;FlushEventSubscriber&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;EventSubscriberInterface&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="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;EntityManagerInterface&lt;/span&gt; &lt;span class="nv"&gt;$entityManager&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;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getSubscribedEvents&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="nc"&gt;KernelEvents&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RESPONSE&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'flush'&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;flush&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;entityManager&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;flush&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;&lt;strong&gt;I haven't tested it, but my guess is, that the &lt;code&gt;flush&lt;/code&gt; method should not take a long time in case no entity has been&lt;br&gt;
changed.&lt;/strong&gt; An alternative approach would be to introduce another &lt;code&gt;FlushInterface&lt;/code&gt; or something similar, that can also be&lt;br&gt;
exchanged based on the used repository implementation.&lt;/p&gt;

&lt;p&gt;The test for this controller can now be implemented something 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="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Tests\Controller&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;App\Domain\Item&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;App\Domain\ItemRepositoryInterface&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;Symfony\Bundle\FrameworkBundle\Test\WebTestCase&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;ItemControllerTest&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;WebTestCase&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;testList&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;$client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="cd"&gt;/** @var ItemRepositoryInterface */&lt;/span&gt;
        &lt;span class="nv"&gt;$itemRepository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&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="nc"&gt;ItemRepositoryInterface&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="nv"&gt;$itemRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&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;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Title 1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Description 1'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="nv"&gt;$itemRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&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;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Title 2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Description 2'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'/items'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$responseContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getResponse&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;getContent&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;assertNotFalse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$responseContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$responseData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$responseContent&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;assertIsArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$responseData&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;assertCount&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;$responseData&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="s1"&gt;'Title 1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$responseData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&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="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="s1"&gt;'Description 1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$responseData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;description&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="s1"&gt;'Title 2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$responseData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&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="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="s1"&gt;'Description 2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$responseData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;description&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;testListWithTitleFilter&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;$client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="cd"&gt;/** @var ItemRepositoryInterface */&lt;/span&gt;
        &lt;span class="nv"&gt;$itemRepository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&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="nc"&gt;ItemRepositoryInterface&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="nv"&gt;$itemRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&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;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Test title 1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Description 1'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="nv"&gt;$itemRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&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;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Title 2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Description 2'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="nv"&gt;$itemRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&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;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Test title 3'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Description 3'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'/items?titleFilter=Test title'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$responseContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getResponse&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;getContent&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;assertNotFalse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$responseContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$responseData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$responseContent&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;assertIsArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$responseData&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;assertCount&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;$responseData&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="s1"&gt;'Test title 1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$responseData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&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="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="s1"&gt;'Description 1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$responseData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;description&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="s1"&gt;'Test title 3'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$responseData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&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="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="s1"&gt;'Description 3'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$responseData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;description&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;testCreate&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;$client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="cd"&gt;/** @var ItemRepositoryInterface */&lt;/span&gt;
        &lt;span class="nv"&gt;$itemRepository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&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="nc"&gt;ItemRepositoryInterface&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="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;jsonRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'POST'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'/items'&lt;/span&gt;&lt;span class="p"&gt;,&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="s1"&gt;'Title'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'description'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Description'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="nv"&gt;$items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$itemRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;loadAll&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;assertCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$items&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="s1"&gt;'Title'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&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;getTitle&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="s1"&gt;'Description'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&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;getDescription&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;I will not go into every detail of testing in Symfony (the &lt;a href="https://symfony.com/doc/current/testing.html" rel="noopener noreferrer"&gt;Symfony testing&lt;br&gt;
documentation&lt;/a&gt; already does a decent job at this), instead, I will only&lt;br&gt;
talk about the highlight: &lt;strong&gt;This test relies on the &lt;code&gt;ItemRepositoryInterface&lt;/code&gt; instead of the Doctrine one.&lt;/strong&gt; It is used&lt;br&gt;
to setup some data in the &lt;code&gt;testList&lt;/code&gt; and &lt;code&gt;testListWithTitleFilter&lt;/code&gt; tests and also to assert if data was actually stored&lt;br&gt;
&lt;code&gt;testCreate&lt;/code&gt;. If the tests are run like this they will not often succeed, since the database is never reset. However,&lt;br&gt;
the goal of this blog post is not to use databases for this kind of test anyway. Therefore a &lt;code&gt;config/services_test.yaml&lt;/code&gt;&lt;br&gt;
file is created instead, which contains the following lines:&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;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;App\Domain\ItemRepositoryInterface&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@App\Repository\Memory\ItemRepository'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way for all tests the &lt;code&gt;ItemRepository&lt;/code&gt; using just an array as memory is used whenever the&lt;br&gt;
&lt;code&gt;ItemRepositoryInterface&lt;/code&gt; is being referred. This means that with this configuration no database at all is used in the&lt;br&gt;
above test for the controller, which makes the tests incredibly fast. &lt;strong&gt;At the same time, these tests are quite&lt;br&gt;
reliable since the memory implementation behaves like the Doctrine implementation because of the&lt;br&gt;
&lt;code&gt;AbstractItemRepositoryTest&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The only test actually running against the database is the &lt;code&gt;ItemRepositoryTest&lt;/code&gt; for the Doctrine implementation, which&lt;br&gt;
only injects the &lt;code&gt;EntityManagerInterface&lt;/code&gt;, for which reason the configuration in &lt;code&gt;services_test.yaml&lt;/code&gt; does not apply in&lt;br&gt;
this case.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;In summary, I can say that I have never been so happy with my tests.&lt;/strong&gt; They are incredibly fast, give me a lot of&lt;br&gt;
confidence since the memory implementation should behave very similar to the Doctrine implementation, and there is no&lt;br&gt;
need to redefine a lot of expectations in many tests as would be the case with mocks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The only downside I can think of is that in the case of repositories complex queries might be hard to implement using&lt;br&gt;
just an array, but in my opinion, this is not a real deal breaker.&lt;/strong&gt; And quite often some calls to array methods like&lt;br&gt;
&lt;code&gt;array_filter&lt;/code&gt; already go a long way in this regard.&lt;/p&gt;

&lt;p&gt;I encourage you to try this kind of testing in a project and I am sure that you will not regret it!&lt;/p&gt;

</description>
      <category>testing</category>
      <category>php</category>
      <category>symfony</category>
    </item>
    <item>
      <title>Making the shell history more useful by using shell variables</title>
      <dc:creator>Daniel Rotter</dc:creator>
      <pubDate>Mon, 18 Sep 2023 13:05:08 +0000</pubDate>
      <link>https://dev.to/danrot90/making-the-shell-history-more-useful-by-using-shell-variables-3f1g</link>
      <guid>https://dev.to/danrot90/making-the-shell-history-more-useful-by-using-shell-variables-3f1g</guid>
      <description>&lt;p&gt;I already described &lt;a href="///2023/07/19/combine-jq-with-curl-to-improve-its-json-handling.html"&gt;that I like using curl for testing HTTP&lt;br&gt;
APIs&lt;/a&gt;. I have also used that approach a lot lately,&lt;br&gt;
but with an API that required some kind of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication" rel="noopener noreferrer"&gt;HTTP&lt;br&gt;
authentication&lt;/a&gt;. This is also no problem for&lt;br&gt;
&lt;a href="https://curl.se/" rel="noopener noreferrer"&gt;curl&lt;/a&gt;, which can send the necessary &lt;code&gt;Authorization&lt;/code&gt; header using its &lt;code&gt;-H&lt;/code&gt; flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; GET &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer some-lengthy-but-not-infinitely-valid-token"&lt;/span&gt; localhost/some-uuid-that-might-be-valid-only-once
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While this basically works, it is still problematic from a shell history perspective. I already hinted at the problems&lt;br&gt;
in the placeholder values in the above &lt;code&gt;curl&lt;/code&gt; command. &lt;strong&gt;Re-executing this command only works easily as long as the&lt;br&gt;
passed values in headers and URL are valid.&lt;/strong&gt; In case the token or the passed value changes frequently it becomes very&lt;br&gt;
tedious to reuse the command from history, since after pressing up until the desired command appears you must fiddle&lt;br&gt;
around (moving the cursor, deleting some characters, inserting new ones, ...) to replace values which became invalid&lt;br&gt;
with valid ones. In my case this mostly was about the bearer token, which I had to retrieve again after it became&lt;br&gt;
invalid, giving me a hard time reusing existing commands.&lt;/p&gt;

&lt;p&gt;However, I just realized recently that there is quite an easy fix for that. &lt;strong&gt;By placing those changing values in shell&lt;br&gt;
variables the command in the history is much easier to reuse.&lt;/strong&gt; So instead of copying tokens, UUIDs, etc. directly into&lt;br&gt;
the command I set them as shell variables and use these shell variables in the command. The following examples work with&lt;br&gt;
the &lt;a href="https://fishshell.com/" rel="noopener noreferrer"&gt;fish shell&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;&lt;span class="nb"&gt;set &lt;/span&gt;TOKEN some-lengthy-but-not-infinitely-valid-token
&lt;span class="nb"&gt;set &lt;/span&gt;UUID some-uuid-that-might-be-valid-only-once

curl &lt;span class="nt"&gt;-X&lt;/span&gt; GET &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; localhost/&lt;span class="nv"&gt;$UUID&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This might look like more work for the first command (who am I  kidding, it also is more work for the first command),&lt;br&gt;
but the huge advantage is that the last command can be easily reused. &lt;strong&gt;Instead of pressing up and manually moving the&lt;br&gt;
cursor and replacing parameters you can set new values to the shell variables and execute the command from history as&lt;br&gt;
is.&lt;/strong&gt; So the workflow after retrieving a new token and UUID is as follows:&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="nb"&gt;set &lt;/span&gt;TOKEN another-lengthy-but-not-infinitely-valid-token
&lt;span class="nb"&gt;set &lt;/span&gt;UUID another-uuid-that-might-be-valid-only-once

&lt;span class="c"&gt;# Press up to get the curl command in history and execute it right away&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This might not look like a big deal now, but especially if the command to execute is more complex this gets really&lt;br&gt;
useful. To give an even better example, I recently worked on an HTTP API using authentication, which had a use case&lt;br&gt;
involving a two-step process, i.e. receiving a token from the API that has to be sent in another request. For testing I&lt;br&gt;
executed two &lt;code&gt;curl&lt;/code&gt; commands at once by using command substitution, whereby both are using the same shell variables for&lt;br&gt;
the token and the inner one &lt;a href="///2023/07/19/combine-jq-with-curl-to-improve-its-json-handling.html"&gt;uses &lt;code&gt;jq&lt;/code&gt; as described in my previous blog&lt;br&gt;
post&lt;/a&gt; to only retrieve the value I need for the&lt;br&gt;
second &lt;code&gt;curl&lt;/code&gt; command (the used &lt;code&gt;-r&lt;/code&gt; flag for &lt;code&gt;jq&lt;/code&gt; returns the raw value, not a beautified one, which would probably&lt;br&gt;
make problems when used with other commands). In fish &lt;a href="https://fishshell.com/docs/current/tutorial.html#command-substitutions" rel="noopener noreferrer"&gt;command substitution uses parentheses instead of backticks as in&lt;br&gt;
e.g. bash&lt;/a&gt;, but the idea is pretty much the same&lt;br&gt;
in any shell. This is how such a command can look like:&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="nb"&gt;set &lt;/span&gt;TOKEN some-lengthy-but-not-infinitely-valid-token

curl &lt;span class="nt"&gt;-X&lt;/span&gt; GET &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; localhost/item/&lt;span class="o"&gt;(&lt;/span&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; localhost/item | jq &lt;span class="nt"&gt;-r&lt;/span&gt; .id&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, using the environment variable makes even more sense since the token has to be used twice in the same&lt;br&gt;
command, which would make replacing it even more cumbersome.&lt;/p&gt;

&lt;p&gt;This small change in writing shell commands can make a huge difference when re-executing commands multiple times, for&lt;br&gt;
which reason I wanted to share it with you!&lt;/p&gt;

</description>
      <category>cli</category>
      <category>linux</category>
      <category>fish</category>
    </item>
    <item>
      <title>Combine jq with curl to improve its JSON handling</title>
      <dc:creator>Daniel Rotter</dc:creator>
      <pubDate>Fri, 04 Aug 2023 09:03:07 +0000</pubDate>
      <link>https://dev.to/danrot90/combine-jq-with-curl-to-improve-its-json-handling-f9n</link>
      <guid>https://dev.to/danrot90/combine-jq-with-curl-to-improve-its-json-handling-f9n</guid>
      <description>&lt;p&gt;As you might have realized in some of my other blog posts I am a bit of a CLI aficionado, and for this reason, I do not&lt;br&gt;
like tools like &lt;a href="https://www.postman.com/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt; despite their rich feature set. To be precise, I don't like them&lt;br&gt;
exactly for their rich feature set, since it takes me ages to find the exact option I am looking for. Finding the right&lt;br&gt;
option might be as hard in CLI tools as in GUI tools, but once the option has been discovered, there are much better&lt;br&gt;
ways to reuse them (creating an alias, a script, finding it in your shell history, ...).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There is an obvious and well-known CLI replacement for at least some of Postman's functionality:&lt;br&gt;
&lt;a href="https://curl.se/" rel="noopener noreferrer"&gt;curl&lt;/a&gt;.&lt;/strong&gt; In my opinion, it is even better, since it comes with a - at least compared to Postman -&lt;br&gt;
reduced feature set, but following the Unix philosophy, it is possible to use it in combination with other commands.&lt;/p&gt;

&lt;p&gt;Another option I know is &lt;a href="https://httpie.io/cli" rel="noopener noreferrer"&gt;HTTPie&lt;/a&gt;, which is like curl on steroids focused on APIs. It comes with&lt;br&gt;
a CLI API that is simpler than curl's and has other amazing features like built-in JSON highlighting. However, I still&lt;br&gt;
decided to continue using curl, since the arguments I need are not that numerous and were not too hard to remember once&lt;br&gt;
I really tried. Also, it is kind of the industry standard, so it is easier to find documentation for it, and it is often&lt;br&gt;
better integrated in other software, e.g. &lt;strong&gt;most browsers have an option in their developer tools to copy network&lt;br&gt;
requests as curl commands, which includes all headers, cookies, etc. that were included in that request, which makes&lt;br&gt;
seven testing an API that needs authentication&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fus9eutcspufquhbeanoc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fus9eutcspufquhbeanoc.png" alt="The " width="800" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I do not know of any browser offering a similar option for HTTPie, and that alone is enough reason for me to stick with&lt;br&gt;
curl.&lt;/p&gt;

&lt;p&gt;Fortunately, there is the &lt;a href="https://jqlang.github.io/jq/" rel="noopener noreferrer"&gt;&lt;code&gt;jq&lt;/code&gt;&lt;/a&gt;, which is a command-line JSON processor. The great thing&lt;br&gt;
about it is that it allows you to pretty print JSON, filter and/or transform it, etc., but does not care about where it&lt;br&gt;
gets its input from. &lt;strong&gt;Therefore it is very versatile and can be used in many different scenarios.&lt;/strong&gt; One scenario for&lt;br&gt;
which I used it recently is to pretty print the log output of a Kubernetes container, which used JSON as log format. It&lt;br&gt;
was hardly possible to read that output, therefore I used &lt;code&gt;jq&lt;/code&gt; without any arguments to pretty-print the output, which&lt;br&gt;
results in syntax-highlighted and properly formatted text that is easy to read. The example command &lt;code&gt;cat log.json | jq&lt;/code&gt;&lt;br&gt;
in the following screenshot uses a simple file for demonstration purposes, but it does not matter where the command&lt;br&gt;
before the pipe gets the content from, as long as it outputs it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fvhhkrp6ely1b3fynaqcg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fvhhkrp6ely1b3fynaqcg.png" alt=" raw `jq` endraw  highlighting JSON results in a much easier to read text" width="800" height="625"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;jq&lt;/code&gt; would even allow filtering e.g. by the level of the log message using its &lt;a href="https://jqlang.github.io/jq/manual/#select(boolean_expression)" rel="noopener noreferrer"&gt;&lt;code&gt;select&lt;/code&gt;&lt;br&gt;
function&lt;/a&gt;, and there are many other possibilities.&lt;/p&gt;

&lt;p&gt;Combining &lt;code&gt;jq&lt;/code&gt; with the &lt;code&gt;curl&lt;/code&gt; command results in a JSON output from an HTTP API to be shown properly indented and with&lt;br&gt;
syntax highlighting, which is one of the best features of HTTPie. The command for this would be something like that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl http://127.0.0.1:8000/something.json | jq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But it does not even stop there. &lt;code&gt;jq&lt;/code&gt; also allows filtering deeply nested JSON objects, which I found particularly handy&lt;br&gt;
when I was working at &lt;a href="https://sulu.io/" rel="noopener noreferrer"&gt;Sulu&lt;/a&gt;. There we implemented a configuration request, which was used by the&lt;br&gt;
administration SPA to retrieve quite some initial data. This configuration request also contained configuration data&lt;br&gt;
from different modules, so it looked somehow like this (omitted a lot of data in objects and arrays to keep it brief):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sulu_admin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"fieldTypeOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"internalLinkTypes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"localizations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"navigation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"routes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"resources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"smartContent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"contact"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"collaborationEnabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"collaborationInterval"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20000&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sulu_contact"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sulu_media"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sulu_page"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sulu_website"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sulu_preview"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sulu_trash"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sulu_security"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So when I implemented something that should update a value in this big configuration I found it quite hard to find the&lt;br&gt;
desired value when requesting the data in the browser. So what I did instead was to use &lt;code&gt;jq&lt;/code&gt; to filter for the exact&lt;br&gt;
value I needed to see.&lt;/p&gt;

&lt;p&gt;If I e.g. wanted to check if the &lt;code&gt;collaborationInterval&lt;/code&gt; was adjusted accordingly I used the "Copy as curl" option from&lt;br&gt;
my browser's developer tools, pasted it into the terminal (in the example below I omitted all HTTP headers for brevity,&lt;br&gt;
although depending on your application some might be necessary), and piped it through a &lt;code&gt;jq&lt;/code&gt; command with a filter only&lt;br&gt;
returning the &lt;code&gt;collaborationInterval&lt;/code&gt; value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl 'https://127.0.0.1:8000/admin/config' | jq .sulu_admin.collaborationInterval
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;This allows me to just executed the last command in my history, again and again, to see if the value I was trying to&lt;br&gt;
update.&lt;/strong&gt; And that even without looking for it in a huge pile of JSON.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;jq&lt;/code&gt; is a really powerful tool, and there are many more opportunities to use it. I highly recommend checking out the &lt;a href="https://jqlang.github.io/jq/manual/" rel="noopener noreferrer"&gt;jq&lt;br&gt;
Manual&lt;/a&gt; if you are working with JSON a lot, I am sure there are some ways you can&lt;br&gt;
benefit from it!&lt;/p&gt;

</description>
      <category>cli</category>
      <category>http</category>
      <category>json</category>
      <category>logging</category>
    </item>
    <item>
      <title>Use external programs like git in Neovim commands</title>
      <dc:creator>Daniel Rotter</dc:creator>
      <pubDate>Sat, 15 Jul 2023 08:50:16 +0000</pubDate>
      <link>https://dev.to/danrot90/use-external-programs-like-git-in-neovim-commands-3kpf</link>
      <guid>https://dev.to/danrot90/use-external-programs-like-git-in-neovim-commands-3kpf</guid>
      <description>&lt;p&gt;I have been an enthusiastic (Neo)vim user for years now, and until today I love to improve my setup and often learn one or the other trick that makes me more efficient, even if it is just by a little bit. Therefore I want to start writing about that as well.&lt;/p&gt;

&lt;p&gt;Recently I started using the command mode of Neovim a lot more, and do not stop at the most basic commands like &lt;code&gt;:w&lt;/code&gt; to save a file or the infamous &lt;code&gt;:q&lt;/code&gt; for exiting it (why would you want to exit such a great program anyway). &lt;strong&gt;What I find particularly interesting is the fact that you can use any command installed on the machine in the command mode by starting the line with a bang.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I usually have an instance of Neovim running within the awesome &lt;a href="https://sw.kovidgoyal.net/kitty/" rel="noopener noreferrer"&gt;kitty terminal&lt;/a&gt;, along with a few other windows running shells. These other shells I use to run all kinds of commands, like &lt;code&gt;git status&lt;/code&gt;, &lt;code&gt;docker compose up&lt;/code&gt;, etc. I do not plan to get rid of those windows, since the main purpose of an editor is not to run commands, for which reason it can feel clunky sometimes. So while it is possible to execute &lt;code&gt;ls&lt;/code&gt; in Neovim and get the directory listed, it does not feel right to me.&lt;/p&gt;

&lt;p&gt;However, there are some commands, mainly those without any meaningful output at all, that are more comfortable to use from within Neovim. &lt;strong&gt;This is especially true for commands that require some input Neovim can deliver, like the path of the currently opened file, which can be inserted using the &lt;code&gt;%&lt;/code&gt; placeholder&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;E.g. I am still not using a GUI for Git, since I feel much more efficient this way. But staging a specific file using the &lt;code&gt;git add&lt;/code&gt; command always felt a bit tedious, since the path has to be passed as an argument. Since this command makes use of the file path and has no output, I started to execute that command directly in Neovim instead of typing it into the shell, which was cumbersome even with tab completion.&lt;/p&gt;

&lt;p&gt;To do so, I type the following when the file I want to stage is currently opened in normal mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:! git add %
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way the currently opened file will be staged, without providing the entire file path! Doing this feels so natural that installing a separate plugin for Git handling almost feels like a waste of resources (at least if you only need it for adding files to the staging area).&lt;/p&gt;

&lt;p&gt;There is another use case I am using quite often lately: If I am currently editing a test I usually want to execute it right afterward, ideally only this single test file instead of my entire suite. Most test runners support that by passing the path of the test file as an argument. But again, doing so felt quite tedious to me. Fortunately, that task can also be improved by using a Neovim command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:! echo % | pbcopy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here the built-in &lt;code&gt;echo&lt;/code&gt; command is used to output the current file path using the &lt;code&gt;%&lt;/code&gt; placeholder to &lt;code&gt;stdout&lt;/code&gt;, which will then be piped to &lt;code&gt;stdin&lt;/code&gt; of the &lt;code&gt;pbcopy&lt;/code&gt; command. &lt;code&gt;pbcopy&lt;/code&gt; comes with macOS, and will put anything it gets via &lt;code&gt;stdin&lt;/code&gt; into the clipboard. So instead of typing the command for my test runner and tediously passing the file path as an argument I can now only type the command of the test runner and pass the test file path by pasting it from the clipboard using &lt;code&gt;command+v&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In addition, commands like that can also be used to alter the text in the editor. This is different from the uses before since commands need a meaningful output for that to make sense, but it shows how extremely powerful that concept is. An example of that is the following command, which inserts the current date using the &lt;code&gt;date&lt;/code&gt; command from the operating system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:read !date
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;read&lt;/code&gt; is a Neovim command, that can be used to insert text at the current cursor position. And this &lt;code&gt;read&lt;/code&gt; command can also be used in combination with any operating system command like &lt;code&gt;date&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It is also not only possible to insert text, but also to manipulate existing text using such commands. Imagine you have a list of numbers in your editor, that you would like to sort. Instead of building some special functionality in Neovim, it is quite easy to use the already existing &lt;code&gt;sort&lt;/code&gt; command. This can be done by marking the numbers using the visual mode (pressing &lt;code&gt;v&lt;/code&gt; in normal mode) and then execute the following command (do not worry, the &lt;code&gt;'&amp;lt;,'&amp;gt;&lt;/code&gt; will be inserted automatically):&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;,'&amp;gt;! sort -n
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This shows that it even is possible to pass any arguments to the shell commands.&lt;/p&gt;

&lt;p&gt;I love this about this entire ecosystem since I always get excited when different systems can be combined so easily. Do you have any other ideas on how to use this approach?&lt;/p&gt;

</description>
      <category>neovim</category>
      <category>vim</category>
      <category>git</category>
      <category>cli</category>
    </item>
  </channel>
</rss>
