<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="4.3.4">Jekyll</generator><link href="https://ziumper.github.io/feed.xml" rel="self" type="application/atom+xml"/><link href="https://ziumper.github.io/" rel="alternate" type="text/html" hreflang="en"/><updated>2025-12-10T17:23:54+00:00</updated><id>https://ziumper.github.io/feed.xml</id><title type="html">Ziumper</title><entry><title type="html">New n8n Tool!</title><link href="https://ziumper.github.io/blog/2025/n8n-automation-tool/" rel="alternate" type="text/html" title="New n8n Tool!"/><published>2025-10-09T00:00:00+00:00</published><updated>2025-10-09T00:00:00+00:00</updated><id>https://ziumper.github.io/blog/2025/n8n-automation-tool</id><content type="html" xml:base="https://ziumper.github.io/blog/2025/n8n-automation-tool/"><![CDATA[<h2 id="what-is-n8n">What is n8n?</h2> <p>n8n is a process automation tool, with a particular focus on AI-related automations.</p> <p>Its key features include:</p> <ul> <li>has a community (open source) version. <a href="https://github.com/n8n-io/n8n">Repository link</a></li> <li>written in TypeScript</li> <li>strongly resembles blueprint programming from Unreal Engine</li> <li>offers many ready-made nodes and integrations</li> <li>very easy to use (drag and drop)</li> <li>allows you to combine processes into sub-processes</li> <li>if you know <a href="https://www.bpmn.org/">BPMN</a>, you’ll quickly understand how it works</li> <li>enables you to write code in the built-in editor using JavaScript and Python</li> </ul> <h2 id="working-with-n8n">Working with n8n</h2> <p>One of the most important things I realized while working with n8n was how crucial it is to ensure the correct structure of input and output data. It provides options for collecting, aggregating, and filtering data. Data processing is inherently iterative.</p> <p>Writing code is quite convenient – there are suggestions and autocompletion. I had to adjust a bit and use JavaScript blocks to ensure proper hydration and data structure.</p> <h2 id="what-can-you-use-it-for">What can you use it for?</h2> <p>I think it’s great for:</p> <ul> <li>orchestration and automation of processes</li> <li>automating repetitive tasks</li> <li>quick integration of popular tools</li> <li>data synchronization</li> </ul> <h2 id="example-use-case">Example use case</h2> <p>With n8n, I automated the process of fetching new entries from an RSS feed and saving them to Google Sheets. Thanks to the ready-made integrations, it was enough to connect a few nodes, set up a schedule, and everything works automatically.</p> <h2 id="drawbacks-and-limitations">Drawbacks and limitations</h2> <p>During my tests, I noticed that:</p> <ul> <li>some integrations require additional configuration or API keys,</li> <li>the web interface can sometimes be less responsive with large workflows,</li> <li>the documentation is extensive, but not always detailed for less popular nodes.</li> </ul> <h2 id="summary">Summary</h2> <p>n8n is a great tool for people who want to quickly implement automations without writing a lot of code. I especially recommend it to those who value flexibility and the ability to expand their own workflows.</p>]]></content><author><name></name></author><category term="n8n"/><summary type="html"><![CDATA[Recently, I had the opportunity to play with the hosted version of n8n and wanted to share my impressions.]]></summary></entry><entry><title type="html">Alternative IDE for PHP? Apache NetBeans, but not only!</title><link href="https://ziumper.github.io/blog/2025/alternative-ide-for-php/" rel="alternate" type="text/html" title="Alternative IDE for PHP? Apache NetBeans, but not only!"/><published>2025-10-08T00:00:00+00:00</published><updated>2025-10-08T00:00:00+00:00</updated><id>https://ziumper.github.io/blog/2025/alternative-ide-for-php</id><content type="html" xml:base="https://ziumper.github.io/blog/2025/alternative-ide-for-php/"><![CDATA[<p>This post will be a bit longer because I want to present the challenges and the journey this IDE has taken from its old software to its newer version. I also want to focus on its drawbacks and, in a sense, show what working in Apache NetBeans looks like today. When did I come across it? Why did I start using it only recently, and why does it seem that this IDE is making a comeback? I will also try to present how to configure the IDE and my first impressions.</p> <h2 id="apache-netbeans">Apache NetBeans</h2> <p>To begin, it is worth mentioning the history of Apache NetBeans. It is an integrated development environment that was originally created mainly for Java programming, but now supports several programming languages, including PHP. The general timeline is as follows:</p> <ul> <li><strong>1996</strong> – At Charles University in Prague, a group of students starts the Xelfi project, one of the first Java IDEs written in Java.</li> <li><strong>1997-1998</strong> – The Xelfi project is developed further, and its name changes to NetBeans.</li> <li><strong>1999</strong> – Sun Microsystems buys NetBeans from the creators in Prague and begins official development, releasing it as open-source software.</li> <li><strong>2000-2010</strong> – NetBeans develops rapidly, gaining support for new Java versions and other technologies. It becomes one of the most popular IDEs for Java developers.</li> <li><strong>2010</strong> – Oracle acquires Sun Microsystems and thus becomes the owner of the NetBeans project.</li> <li><strong>2016</strong> – Oracle decides to transfer NetBeans to the Apache Software Foundation, starting the project’s incubation process.</li> <li><strong>2018</strong> – NetBeans officially becomes an Apache project and is named Apache NetBeans. The first version under the Apache name is released – Apache NetBeans 9.0.</li> <li><strong>2018-present</strong> – Apache NetBeans is actively developed by the open-source community. New versions are released regularly, supporting the latest technologies and programming languages.</li> </ul> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/posts/netbeans/netbeans_ide-480.webp 480w,/assets/img/posts/netbeans/netbeans_ide-800.webp 800w,/assets/img/posts/netbeans/netbeans_ide-1400.webp 1400w," sizes="95vw" type="image/webp"/> <img src="/assets/img/posts/netbeans/netbeans_ide.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" data-zoomable="" loading="lazy" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <p>Configuring NetBeans is relatively simple, though it requires a few steps: NetBeans can be downloaded directly from the Apache website. The installer is available for all popular operating systems. The most convenient way is to download Apache NetBeans directly from the <a href="https://github.com/apache/netbeans/releases">repository</a>.</p> <p>After installation, it is worth reviewing the available plugins. NetBeans natively supports PHP, but you can install support for other languages. Above all, I recommend enabling the default PHP plugin.</p> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/posts/netbeans/netbeans_php_plugin-480.webp 480w,/assets/img/posts/netbeans/netbeans_php_plugin-800.webp 800w,/assets/img/posts/netbeans/netbeans_php_plugin-1400.webp 1400w," sizes="95vw" type="image/webp"/> <img src="/assets/img/posts/netbeans/netbeans_php_plugin.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" data-zoomable="" loading="lazy" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <p>Additionally, I recommend installing this plugin via GitHub: <a href="https://github.com/junichi11/netbeans-php-enhancements/releases">netbeans-php-enhancements</a> or through the plugin portal <a href="https://plugins.netbeans.apache.org/catalogue/?id=29">plugin link</a>.</p> <p>To install a plugin, simply download and unpack it, then go to Tools -&gt; Plugins -&gt; Downloaded and select the file in the *.nbm format.</p> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/posts/netbeans/plugin_instruction-480.webp 480w,/assets/img/posts/netbeans/plugin_instruction-800.webp 800w,/assets/img/posts/netbeans/plugin_instruction-1400.webp 1400w," sizes="95vw" type="image/webp"/> <img src="/assets/img/posts/netbeans/plugin_instruction.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" data-zoomable="" loading="lazy" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <p>After installation, it should look as follows:</p> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/posts/netbeans/php-enhancments-480.webp 480w,/assets/img/posts/netbeans/php-enhancments-800.webp 800w,/assets/img/posts/netbeans/php-enhancments-1400.webp 1400w," sizes="95vw" type="image/webp"/> <img src="/assets/img/posts/netbeans/php-enhancments.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" data-zoomable="" loading="lazy" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <p>In the settings, you need to specify the path to the PHP interpreter</p> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/posts/netbeans/cli_inerpreter-480.webp 480w,/assets/img/posts/netbeans/cli_inerpreter-800.webp 800w,/assets/img/posts/netbeans/cli_inerpreter-1400.webp 1400w," sizes="95vw" type="image/webp"/> <img src="/assets/img/posts/netbeans/cli_inerpreter.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" data-zoomable="" loading="lazy" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <p>and enable additional options and hints for the latest plugin</p> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/posts/netbeans/php-config-enhance-480.webp 480w,/assets/img/posts/netbeans/php-config-enhance-800.webp 800w,/assets/img/posts/netbeans/php-config-enhance-1400.webp 1400w," sizes="95vw" type="image/webp"/> <img src="/assets/img/posts/netbeans/php-config-enhance.png" width="100%" height="auto" loading="lazy" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <h3 id="debugging">Debugging</h3> <p>When it comes to debugging, we have the following option.</p> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/posts/netbeans/Debugging-480.webp 480w,/assets/img/posts/netbeans/Debugging-800.webp 800w,/assets/img/posts/netbeans/Debugging-1400.webp 1400w," sizes="95vw" type="image/webp"/> <img src="/assets/img/posts/netbeans/Debugging.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" data-zoomable="" loading="lazy" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <p>You also need to install the <a href="https://chromewebstore.google.com/detail/xdebug-helper-by-jetbrain/aoelhdemabeimdhedkidlnbkfhnhgnhm">Xdebug Helper</a>. and set the appropriate IDE key.</p> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/posts/netbeans/xdebug_netbeans-480.webp 480w,/assets/img/posts/netbeans/xdebug_netbeans-800.webp 800w,/assets/img/posts/netbeans/xdebug_netbeans-1400.webp 1400w," sizes="95vw" type="image/webp"/> <img src="/assets/img/posts/netbeans/xdebug_netbeans.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" data-zoomable="" loading="lazy" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <p>In the case of debugging with Docker, we can run the test code on Docker as follows:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker compose exec -e XDEBUG_MODE=debug -e XDEBUG_CONFIG="start_with_request=trigger idekey=netbeans client_host=host.docker.internal client_port=9003" myServiceInDocker vendor/bin/phpunit'
</code></pre></div></div> <h3 id="writing-code">Writing Code</h3> <p>When it comes to writing PHP code, you have to adjust a bit:</p> <ul> <li>importing all dependencies using the keyboard shortcut <code class="language-plaintext highlighter-rouge">Ctrl + Shift + I</code> instead of PHPStorm’s <code class="language-plaintext highlighter-rouge">alt+enter</code>. In my opinion, this is even better as it opens a dialog window where we can decide which dependencies we should import.</li> <li>searching using “Ctrl + I” in the top right corner</li> <li>switching between files using “Ctrl + PageUp” and “Ctrl + PageDown”</li> <li>php doc <code class="language-plaintext highlighter-rouge">/** @var MyClass $myVariable */</code> works only in one line.</li> </ul> <p>There are some quirks and borrowings from other IDEs. Ultimately, it can be used if you really want to. Netbeans also provides support for the <a href="https://twig.symfony.com/">Twig</a> templating language. Syntax highlighting and auto-complete works, but unfortunately not much. Quick search does not work for <strong>Twig</strong> files and other similar files and requires appropriate modifications of PHP modules or extensions.</p> <h3 id="advantages">Advantages</h3> <ul> <li><strong>Lightweight</strong> – NetBeans is noticeably less resource-intensive than PHPStorm. It runs smoothly even on older computers.</li> <li><strong>Open Source</strong> – Completely free, with no licensing restrictions.</li> <li><strong>Support for multiple languages</strong> – Besides PHP, it handles Java, JavaScript, HTML, C/C++ very well.</li> <li><strong>Built-in tools</strong> – Debugger, profiler, GIT integration, support for frameworks (e.g., Symfony, Laravel).</li> <li><strong>Stability</strong> – It rarely crashes, and updates are regular.</li> <li><strong>Simple configuration</strong> – You can quickly start working without having to install dozens of extensions.</li> <li><strong>Extensibility</strong> - You can quickly start extending your IDE, surprisingly it’s much more convenient than in PHP</li> <li><strong>Blast from the past</strong> - Interestingly, it’s a bit of a blast from the past and you can just feel like you did when programming once, when the IDE didn’t suggest everything</li> <li><strong>Indexing process for PHPStorm</strong> - Currently, it’s simple and efficient enough to use without any problems.</li> <li><strong>Contains a few unique ideas</strong> - Surprisingly, I found a few cool and useful things in it, like the task list or a slightly different style of working with the terminal.</li> <li><strong>Ability to customize themes, keyboard shortcuts, and window layout.</strong></li> </ul> <h3 id="disadvantages">Disadvantages</h3> <ul> <li><strong>Autocomplete</strong> – Works well, but requires the use of PHPDoc. In PHPStorm, the suggestions are more advanced and do not require such detailed documentation.</li> <li><strong>Fewer plugins</strong> – The NetBeans community is smaller than, for example, VSCode, which limits the number of available extensions.</li> <li><strong>Appearance</strong> – The user interface is somewhat outdated compared to the competition.</li> <li><strong>No native integration with Docker</strong> – The environment has to be configured manually, which can be cumbersome when working with containers. PHP interpreter only in the native environment, you can’t connect it from Docker, which is a bit inconvenient.</li> <li><strong>Static code analysis</strong> – It works, but it’s not as developed as in PHPStorm.</li> <li><strong>Fewer educational materials</strong> – It’s harder to find up-to-date tutorials and community support.</li> <li><strong>A bit outdated</strong> - In the general competition, it probably lacks a lot compared to its free alternatives. Especially Visual Studio Code, but I bet this will change over time</li> <li><strong>No native support for Docker</strong> -</li> <li><strong>Debugging</strong> - only on the server, if we want to debug scripts it’s already a bit more complicated and we have to tinker with it.</li> <li><strong>GIT Integration</strong> - NetBeans has built-in GIT support, allowing convenient work with repositories directly from the IDE, but unfortunately, it doesn’t work as I expected, so I recommend getting used to working with the terminal. Probably, we have to wait for bug fixes.</li> <li><strong>AI</strong> - lack of integration with copilot, there is a plugin that simulates it, but it’s not the same as in PHPStorm. It can just as well be a plus ;-)</li> </ul> <h3 id="sample-applications">Sample Applications</h3> <p>NetBeans is great for small and medium PHP projects, where we don’t need advanced refactoring tools or cloud integration. It’s ideal for people who value simplicity and stability, while still wanting access to basic IDE functions.</p> <h2 id="tips-and-tricks">Tips and Tricks</h2> <ul> <li>It’s worth using the “Live Templates” feature for quickly inserting repetitive code snippets.</li> <li>You can configure your own keyboard shortcuts, which significantly speeds up work.</li> <li>NetBeans allows for quick switching between files and classes (Ctrl+O, Ctrl+Shift+O).</li> <li>It’s important to regularly update the IDE and plugins - this improves stability and security.</li> <li>If you’re working with Docker, you’ll probably need to manually configure the environment.</li> </ul> <h2 id="my-experiences-and-recommendations">My Experiences and Recommendations</h2> <p>After a few months of working with NetBeans, I can say that it’s a tool that allows you to focus on coding. It doesn’t distract with an excess of options, while still providing everything needed for daily work. If you’re looking for an alternative to heavy, paid IDEs - it’s worth trying NetBeans. I especially recommend it to those who value simplicity, stability, and open source. However, you need to be patient, as working with this IDE is quite different from current market solutions and gives the impression of a cumbersome tool. I’m sure that over time, the development of Apache NetBeans will speed up and it will start to constitute a real alternative to JETBrains products. Even though most of their products can already be used for free, time passes, companies change hands, and code remains.</p> <h2 id="and-what-about-phpstorm">And what about PHPStorm?</h2> <p>It’s not that PHPStorm is bad. It’s very good! You could also say that it’s even too good. What I like about software is the feeling that I have control over what I’m doing. Unfortunately, in the case of JetBrains products, I miss that. I’ve always liked to see what’s under the hood of the tools, the toys I used as a child, and I guess that’s how it’s going to stay with me.</p> <p>Therefore, I decided to delve deeper into the issue of the IDE itself. However, the closed source code does not really encourage writing plugins. You really have to get into the documentation. Have knowledge in languages like Kotlin or Gradle. This is quite a high entry point for programmers who haven’t had much contact with Java.</p> <p>From a user’s perspective, it’s a very good tool for those who are starting their programming journey with PHP. It has a lot of facilitating “automagic” features, which can give the impression that programming in it is very easy. Unfortunately, it’s not without a cost in computer resources. However, this also depends on how many “facilitations” we need in our work. It’s a very interesting business model, where we make the user dependent on our product. Sounds familiar, right?</p> <p>The more we get, the easier it is for us, the more often we are willing to use this tool, even at a much higher price. Fortunately, in this case, you can always use a text editor and struggle with the place where you forgot to put a semicolon. Tears are shed… Meanwhile, people are already generating entire applications using modern AI tools.</p> <p>Who knows, maybe IDEs should serve something similar. The most important things are ahead of us. Why PHPStorm may not be the best available IDE and, consequently, why it might be worth focusing on alternative options. I would like to take a look at the alternative in the form of Apache NetBeans in this post.</p> <p>On a daily basis, programming tools can be a bit overwhelming. PhpStorm automatically installs a lot of resource-intensive plugins, such as:</p> <ul> <li> <p><a href="https://plugins.jetbrains.com/plugin/17718-github-copilot">Github Copilot</a> - a plugin that allows you to use GitHub Copilot conveniently. It’s quite useful, but its resource intensity with certain configurations can be problematic. And everyone has limited resources, especially if you’re programming on a laptop. In my opinion, it works much better in a limited context, when I use it from the web browser or just use the chat. However, it is useful in <strong>Agent</strong> mode for code generation. Usually, I ask it very silly things, or when I’m not sure if my approach makes sense. Well, because surely it has tried this before, right? Regarding the things I disabled from this plugin, it’s worth mentioning that the autocomplete can really eat up my laptop’s memory and it’s not that useful. You could even say that “Autocomplete” makes you look like an idiot, but that’s probably a topic for another post.</p> </li> <li> <p><a href="https://plugins.jetbrains.com/plugin/22282-jetbrains-ai-assistant">Jetbrains Assistant</a> - Nothing helps in programming like another AI assistant! Moreover, paid and from JetBrains. It is, in a sense, significantly better at debugging and suggesting than its competitor Copilot. No wonder, after all, it’s a certain area to be developed. Of course, as long as there are other products or services that offer similar services. However, it must be said that it needs to be looked into. It’s as resource-intensive as its competitor. It offers a pleasant and more readable interface and I can confidently say that it helped me solve several problems during my daily coding.</p> </li> <li> <p><a href="https://plugins.jetbrains.com/plugin/10925-database-tools-and-sql-for-webstorm">Database Management</a> - This explorer is really convenient. You can manipulate table views, quickly adjust the view, filters, and choose a suitable driver for your database. It summarizes the data very nicely and I can quickly see what I have inside. I can wholeheartedly recommend it if we care about a quick overview of the database. However, this client can also fail. In my case, it was the driver for the database when accessing the AWS MySQL database. I couldn’t delete a record or view the changes made. Tunneling is possible, but it needs to be configured correctly. Anyway, a solid 7/10! But nothing beats the magic of writing queries in the terminal.</p> </li> <li> <p><a href="https://plugins.jetbrains.com/plugin/7724-docker">Docker</a> - I don’t remember the last time I used it, because I do most things in the console.</p> </li> <li> <p><a href="https://plugins.jetbrains.com/plugin/13125-ftp-sftp-webdav-connectivity">FTP</a> - I usually only run this plugin to log into the server. I know I should use the terminal here. And I think I’ll quickly switch to the terminal. It’s worth mentioning the synchronization feature using <strong>rsync</strong>, it has really saved my skin when I had to quickly upload changes or fix a bug in production after a not-so-well-tested deployment.</p> </li> <li> <p><a href="https://plugins.jetbrains.com/plugin/7219-symfony-plugin">Symfony</a> - a lot of useful functions, this is a plugin that I recommend. Especially for beginners and those learning the <a href="https://symfony.com/">Symfony</a> framework.</p> </li> </ul> <p>In summary, plugins are essential if we want to be productive, yet we gain more knowledge by not using these plugins as developers. By solving integration problems and directly interacting with the software, we can understand much better “what the author meant”. It’s a pity that I discovered this only recently. Better late than never!</p> <p>Before I started the project, I had to disable unnecessary addons and restart my laptop five times. Additionally, I had to configure double the amount of memory on the Swap partition to even start screen sharing. I had to properly configure the entire project, ignore the appropriate folders, just to reduce the amount of memory used. This is really worrying, especially when working with larger projects. It delayed and bothered me in my daily work. One might suggest that most people work in PHP using a Mac, which can handle such loads without any problems. I wonder if it’s okay, especially since I only recently started measuring how much power my laptop can consume when running at full capacity. It ended up that after disabling most of the unused plugins and skipping the files generated by the applications, I was finally able to work normally. Probably, you also need to optimize not only the code that we write but also the tools that we use must be properly configured for this purpose. It’s a bit tempting to leave things as we found them.</p> <p>Aside from the resource issues, the second important factor is that even if we have the ability to preview running processes, PHPStorm does many things automatically for us.</p> <ul> <li>Choosing the PHP version from the <code class="language-plaintext highlighter-rouge">composer.json</code> file,</li> <li>Installing dependencies.</li> <li>Fixing configuration errors for us.</li> <li>Auto-importing dependencies</li> <li>Task lists</li> <li>Debugging</li> <li>Code generation (with AI plugins)</li> </ul> <p>So why did I decide to try switching to an older, free alternative?</p> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/posts/netbeans/usage-480.webp 480w,/assets/img/posts/netbeans/usage-800.webp 800w,/assets/img/posts/netbeans/usage-1400.webp 1400w," sizes="95vw" type="image/webp"/> <img src="/assets/img/posts/netbeans/usage.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" data-zoomable="" loading="lazy" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <p>Below is NetBeans and above is PHPStorm… it eats memory like a rabbit eats cabbage :-) This is certainly due to the fact that with great power comes even greater indexation process.</p>]]></content><author><name></name></author><category term="php"/><category term="ide"/><category term="netbeans"/><summary type="html"><![CDATA[Why not PHPStorm? Why not VIM or Visual Studio Code? For some time, I have been searching for a tool that is not overloaded with features, yet is sufficiently useful and powerful to facilitate daily work.]]></summary></entry><entry><title type="html">Comma at the end of function arguments in PHP (trailing comma)</title><link href="https://ziumper.github.io/blog/2025/trailing-comma-line/" rel="alternate" type="text/html" title="Comma at the end of function arguments in PHP (trailing comma)"/><published>2025-10-07T00:00:00+00:00</published><updated>2025-10-07T00:00:00+00:00</updated><id>https://ziumper.github.io/blog/2025/trailing-comma-line</id><content type="html" xml:base="https://ziumper.github.io/blog/2025/trailing-comma-line/"><![CDATA[<p>Have you ever added a new argument to a function or constructor in PHP and forgotten to add a comma at the end of the previous line? It’s a common mistake that can lead to unnecessary fixes and frustration.</p> <p>Fortunately, since PHP 7.3 you can use the so-called trailing comma, i.e., a comma after the last argument in function calls, function declarations, and arrays. Thanks to this, when you add a new argument, you don’t have to remember to manually add a comma at the end of the previous line.</p> <h3 id="-example-without-trailing-comma">🛑 Example without trailing comma</h3> <div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">User</span> <span class="p">{</span>
    <span class="k">public</span> <span class="k">function</span> <span class="n">__construct</span><span class="p">(</span>
        <span class="kt">string</span> <span class="nv">$name</span><span class="p">,</span>
        <span class="kt">int</span> <span class="nv">$age</span>
    <span class="p">)</span> <span class="p">{</span>
        <span class="c1">// ...</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div> <p>If you want to add another argument, you have to go back to the previous line and add a comma:</p> <div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">User</span> <span class="p">{</span>
    <span class="k">public</span> <span class="k">function</span> <span class="n">__construct</span><span class="p">(</span>
        <span class="kt">string</span> <span class="nv">$name</span><span class="p">,</span>
        <span class="kt">int</span> <span class="nv">$age</span><span class="p">,</span> <span class="c1">// you have to add a comma</span>
        <span class="kt">string</span> <span class="nv">$email</span>
    <span class="p">)</span> <span class="p">{</span>
        <span class="c1">// ...</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div> <h3 id="-example-with-trailing-comma">✅ Example with trailing comma</h3> <div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">User</span> <span class="p">{</span>
    <span class="k">public</span> <span class="k">function</span> <span class="n">__construct</span><span class="p">(</span>
        <span class="kt">string</span> <span class="nv">$name</span><span class="p">,</span>
        <span class="kt">int</span> <span class="nv">$age</span><span class="p">,</span>
    <span class="p">)</span> <span class="p">{</span>
        <span class="c1">// ...</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div> <p>Now you can simply add another argument without worrying about commas:</p> <div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">User</span> <span class="p">{</span>
    <span class="k">public</span> <span class="k">function</span> <span class="n">__construct</span><span class="p">(</span>
        <span class="kt">string</span> <span class="nv">$name</span><span class="p">,</span>
        <span class="kt">int</span> <span class="nv">$age</span><span class="p">,</span>
        <span class="kt">string</span> <span class="nv">$email</span><span class="p">,</span>
    <span class="p">)</span> <span class="p">{</span>
        <span class="c1">// ...</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div> <h3 id="-since-when">📅 Since when?</h3> <p>Trailing comma in function and method argument lists is available since PHP 7.3. Previously, it was only available in arrays.</p> <h3 id="-summary">📝 Summary</h3> <p>Using the trailing comma:</p> <ul> <li>makes adding and removing arguments easier,</li> <li>reduces the risk of syntax errors,</li> <li>improves code readability in the long run.</li> </ul> <p>It’s worth using this feature, especially in larger projects!</p>]]></content><author><name></name></author><category term="php"/><summary type="html"><![CDATA[How the trailing comma in PHP makes a programmer's life easier when working with function and constructor arguments. ,]]></summary></entry><entry><title type="html">Using array_map() in a Programmer’s Daily Life. PHP Review #7</title><link href="https://ziumper.github.io/blog/2025/array-map-in-php/" rel="alternate" type="text/html" title="Using array_map() in a Programmer’s Daily Life. PHP Review #7"/><published>2025-09-30T00:00:00+00:00</published><updated>2025-09-30T00:00:00+00:00</updated><id>https://ziumper.github.io/blog/2025/array-map-in-php</id><content type="html" xml:base="https://ziumper.github.io/blog/2025/array-map-in-php/"><![CDATA[<p>This article won’t be very long, but it will definitely be useful enough to serve as a good reference for future decisions. Is it worth using <code class="language-plaintext highlighter-rouge">array_map</code>? Definitely yes, if you need to perform some mutation operations on individual elements. For example, it can be:</p> <ul> <li>building subarrays from various values</li> </ul> <div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">php</span> <span class="o">&gt;</span> <span class="nb">var_dump</span><span class="p">(</span><span class="nb">array_map</span><span class="p">(</span><span class="k">static</span> <span class="k">fn</span> <span class="p">(</span><span class="nv">$value</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="s1">'numeric_string'</span> <span class="o">=&gt;</span> <span class="nv">$value</span><span class="p">],</span> <span class="p">[</span><span class="s1">'zero'</span><span class="p">,</span><span class="s1">'one'</span><span class="p">,</span><span class="s1">'two'</span><span class="p">,</span><span class="s1">'three'</span><span class="p">]));</span>
<span class="k">array</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span> <span class="p">{</span>
  <span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">=&gt;</span>
  <span class="k">array</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
    <span class="p">[</span><span class="s2">"numeric_string"</span><span class="p">]</span><span class="o">=&gt;</span>
    <span class="nf">string</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span> <span class="s2">"zero"</span>
  <span class="p">}</span>
  <span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">=&gt;</span>
  <span class="k">array</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
    <span class="p">[</span><span class="s2">"numeric_string"</span><span class="p">]</span><span class="o">=&gt;</span>
    <span class="nf">string</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="s2">"one"</span>
  <span class="p">}</span>
  <span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="o">=&gt;</span>
  <span class="k">array</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
    <span class="p">[</span><span class="s2">"numeric_string"</span><span class="p">]</span><span class="o">=&gt;</span>
    <span class="nf">string</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="s2">"two"</span>
  <span class="p">}</span>
  <span class="p">[</span><span class="mi">3</span><span class="p">]</span><span class="o">=&gt;</span>
  <span class="k">array</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
    <span class="p">[</span><span class="s2">"numeric_string"</span><span class="p">]</span><span class="o">=&gt;</span>
    <span class="nf">string</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="s2">"three"</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div> <ul> <li>extracting values from another array and converting them to something else</li> </ul> <div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ids</span> <span class="o">=</span> <span class="nb">array_map</span><span class="p">(</span><span class="k">static</span> <span class="k">fn</span> <span class="p">(</span><span class="nv">$id</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="s1">'id'</span> <span class="o">=&gt;</span> <span class="nv">$id</span><span class="p">],</span> <span class="nb">array_values</span><span class="p">(</span><span class="nv">$ids</span><span class="p">));</span>
</code></pre></div></div> <ul> <li>returning a new list of values with changed values, without modifying the old list</li> </ul> <div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$newArray</span> <span class="o">=</span> <span class="nb">array_map</span><span class="p">(</span><span class="k">static</span> <span class="k">fn</span> <span class="p">(</span><span class="nv">$item</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nb">str_replace</span><span class="p">(</span><span class="s1">'"'</span><span class="p">,</span><span class="s1">'""'</span><span class="p">,</span><span class="nv">$item</span><span class="p">),</span> <span class="nv">$oldArray</span><span class="p">);</span>
</code></pre></div></div> <p>You can multiply the examples, but the most useful ones can be found here: <a href="https://www.php.net/manual/en/function.array-map.php">link to PHP documentation</a></p>]]></content><author><name></name></author><category term="php"/><summary type="html"><![CDATA[This time I decided to take a look at the most common uses of array_map in my daily code.]]></summary></entry><entry><title type="html">Finally well written tests with composer loading structure</title><link href="https://ziumper.github.io/blog/2025/Composer-with-phpunit-setup/" rel="alternate" type="text/html" title="Finally well written tests with composer loading structure"/><published>2025-09-29T00:00:00+00:00</published><updated>2025-09-29T00:00:00+00:00</updated><id>https://ziumper.github.io/blog/2025/Composer-with-phpunit-setup</id><content type="html" xml:base="https://ziumper.github.io/blog/2025/Composer-with-phpunit-setup/"><![CDATA[<p>I stumbled, crawled on ground while having not proper setup in my old project that I should work on. and then I figured how I should structure my loading properties</p> <p>here is what I had in my project</p> <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"autoload"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"psr-4"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"Ziumper\\App\\"</span><span class="p">:</span><span class="w"> </span><span class="s2">"src/"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Ziumper\\App\\"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tests/"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div> <p>but this way all may classes seems to be loaded inside composer autload map. I knew there must be better way to solve that. Here is what I figured out</p> <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"autoload"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"psr-4"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"Ziumper\\App\\"</span><span class="p">:</span><span class="w"> </span><span class="s2">"src/"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"autoload-dev"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"psr-4"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"Ziumper\\App\\"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tests/"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div> <p>Durring that time I got some really serious issues, I couldn’t declare Traits in my tests folder and it was quite hard feeling to copy paste all that code, then I moved forward with next version. Later I figured out that I had an issues with PS-4 autoloading inside shopware plugins. So I decided to move with following.</p> <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"autoload"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"psr-4"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"Ziumper\\App\\"</span><span class="p">:</span><span class="w"> </span><span class="s2">"src"</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"exclude-from-classmap"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"tests"</span><span class="p">]</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"autoload-dev"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"psr-4"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"Ziumper\\App\\Tests\\Utils\\"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tests/utils"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Ziumper\\App\\Tests\\Unit\\"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tests/unit"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Ziumper\\App\\Tests\\Integration\\"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tests/integration"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div> <p>So how this works is:</p> <ul> <li>all base code declarations are stored inside src folder and those used for tests are inside tests\utils folder. Base test cases traits, data providers too.</li> <li>integration and tests are using src references and are giving me some nice working cases.</li> <li>exclude from classmap is a guard which stands in front of my testing code and doesn’t allow it to get into production loading flow.</li> <li>PSR-4 autoload in Composer loads classes by namespace → folder → filename.</li> <li>PHPUnit use PS-4 not auto generated class map.</li> <li>exclude-from-classmap = guards from loading my utility classes in production</li> <li>no need to write custom bootstrap loading</li> </ul> <p>The class loading process may vary depending on the framework or rules used. For example, in shopware, plugins do not want to load Utils classes, for which you have to write with your own bootstrapper class.</p>]]></content><author><name></name></author><category term="php"/><category term="composer"/><category term="phpunit"/><summary type="html"><![CDATA[This post explains how to organize composer project with phpunit dependecies to not load your test cases into classmap and explaing learnings I figured out durring my workflow]]></summary></entry><entry><title type="html">How integrate GitHub Copilot to generate commit messages automatically?</title><link href="https://ziumper.github.io/blog/2025/Add-github-copilot-auto-commit-message-generation/" rel="alternate" type="text/html" title="How integrate GitHub Copilot to generate commit messages automatically?"/><published>2025-09-24T00:00:00+00:00</published><updated>2025-09-24T00:00:00+00:00</updated><id>https://ziumper.github.io/blog/2025/Add-github-copilot-auto-commit-message-generation</id><content type="html" xml:base="https://ziumper.github.io/blog/2025/Add-github-copilot-auto-commit-message-generation/"><![CDATA[<p>The most important part of working in a team is communication. And one of the most important ways to communicate with your team is through commit messages. A good commit message should be clear, concise, and informative. It should describe what changes were made and why they were made. However, writing good commit messages can be time-consuming and tedious. That’s where GitHub Copilot comes in. GitHub Copilot is an AI-powered code completion tool that can help you write code faster and more efficiently. But did you know that GitHub Copilot can also help you write better commit messages? In this post, we’ll show you how to use GitHub Copilot to generate commit messages automatically.</p> <h2 id="step-1-add-github-copilot-template-to-your-repository">Step 1: Add GitHub Copilot template to your repository</h2> <p>To get started, you need to add a GitHub Copilot template to your repository. This template will provide GitHub Copilot with the necessary context to generate commit messages. You can create a new file in your repository called <code class="language-plaintext highlighter-rouge">.github/git-commit-instructions.md</code> and add the following content:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Git Commit Instructions

When making a commit, please follow these guidelines to ensure clarity and consistency in our commit history.
Provide description in the commit message.
* Describe the changes made in the commit and intended impact on the codebase in new lines.
* Make sure to include:
    * What was changed (e.g., added feature, fixed bug, updated documentation).
    * Why the change was necessary (e.g., to improve performance, fix an issue, enhance user experience).
    * Any relevant details that help understand the context of the change.
* Elaborate on the reasoning behind the changes and any relevant context that may help reviewers understand the
commit better.
</code></pre></div></div> <p>This template provides guidelines for writing good commit messages and will help GitHub Copilot generate better commit messages.</p> <h2 id="step-2-use-github-copilot-to-generate-commit-messages">Step 2: Use GitHub Copilot to generate commit messages</h2> <p>Once you have added the template to your repository, you can start using GitHub Copilot to generate commit messages. When you are ready to make a commit, you can use one of the following IDE editors that support GitHub Copilot:</p> <ul> <li>Visual Studio Code</li> <li>JetBrains IDEs (e.g., IntelliJ IDEA, PyCharm, WebStorm)</li> <li>Neovim</li> </ul> <p>When you click the commit message editor button, GitHub Copilot will suggest a commit message based on the changes you have made and the guidelines provided in the template. GitHub Copilot will automatically suggest a commit message based on the changes you have made and the guidelines provided in the template.</p> <h2 id="conclusion">Conclusion</h2> <p>Using GitHub Copilot to generate commit messages automatically can help you write better commit messages and improve your productivity. By adding a GitHub Copilot template to your repository and using one of the supported IDE editors that support GitHub Copilot, you can easily generate clear and informative commit messages. Give it a try and see how it can improve your workflow! I’m already using it and I love it! 😊</p>]]></content><author><name></name></author><category term="git"/><category term="github"/><category term="productivity"/><category term="copilot"/><summary type="html"><![CDATA[This post explains how to use GitHub Copilot to generate commit messages automatically, improving productivity and ensuring clear commit history.]]></summary></entry><entry><title type="html">🤓 Why do PHP developers love the empty line at the end of the file? PHP Review #6</title><link href="https://ziumper.github.io/blog/2025/New-line-at-the-end-of-php-file/" rel="alternate" type="text/html" title="🤓 Why do PHP developers love the empty line at the end of the file? PHP Review #6"/><published>2025-09-23T00:00:00+00:00</published><updated>2025-09-23T00:00:00+00:00</updated><id>https://ziumper.github.io/blog/2025/New-line-at-the-end-of-php-file</id><content type="html" xml:base="https://ziumper.github.io/blog/2025/New-line-at-the-end-of-php-file/"><![CDATA[<h1 id="why-do-we-add-an-empty-line-at-the-end-of-php-and-twig-files-">Why do we add an empty line at the end of PHP and Twig files? 🤔</h1> <p>Have you ever wondered why developers stubbornly add an empty line at the end of files? Is it some secret tradition? Or maybe a way to fight boredom? The answer is more technical, but there’s a bit of history and… a touch of absurdity! 😄</p> <h2 id="technical-reasons-️">Technical reasons 🛠️</h2> <h3 id="1-posix-standard-">1. <strong>POSIX Standard</strong> 🧑‍💻</h3> <p>Imagine POSIX as a strict teacher in the school of programmers. It says: “Every text file must end with a new line (<code class="language-plaintext highlighter-rouge">\n</code>). End of discussion!” Why? Because tools like <code class="language-plaintext highlighter-rouge">cat</code>, <code class="language-plaintext highlighter-rouge">diff</code>, or <code class="language-plaintext highlighter-rouge">git</code> love order. If a file doesn’t end with a new line, they might get upset and show weird results, e.g., merging the last line of one file with the first of another. It’s like two sentences in a book blending into one – chaos! 📚</p> <p>POSIX (Portable Operating System Interface) was created to unify the behavior of operating systems. Thanks to this, programs can work predictably on different computers. And the empty line at the end of a file is a small gesture towards order and compatibility. ✨</p> <h3 id="2-avoiding-errors-in-php-">2. <strong>Avoiding errors in PHP</strong> 🐘</h3> <p>In PHP, if anything appears after the closing <code class="language-plaintext highlighter-rouge">?&gt;</code> tag – even an innocent space or new line – the server might get angry and send HTTP headers at the wrong moment. The result? The page looks weird, and you spend half a day looking for the bug. The empty line is like a programmer’s lucky charm! 🍀</p> <h3 id="3-better-readability-">3. <strong>Better readability</strong> 👀</h3> <p>An empty line at the end of a file is like a period at the end of a sentence. Thanks to it, the code looks neat, and changes in version control systems are clearer. Git likes it when everything is in its place! 😎</p> <h3 id="4-psr-2-and-psr-12-standards-">4. <strong>PSR-2 and PSR-12 Standards</strong> 📏</h3> <p>And what does PHP-FIG, the programmers’ council of elders, say? They invented the PSR-2 standard, and then its younger, more demanding brother PSR-12. Both clearly state: there must be a new line at the end of the file! This way, your code complies with the guidelines, and your linter doesn’t have a panic attack. It’s a bit like following health and safety rules in code – nobody wants to get a ticket from the reviewer! 🚨</p> <h2 id="historical-reasons-">Historical reasons 🏺</h2> <p>In the past, text editors were moody. The lack of an empty line could cause errors during compilation or code interpretation. So developers learned to add this magic line to avoid surprises. It’s a bit like wearing socks with sandals – not always needed and considered a fashion crime, but sometimes it saves the day! 🧦</p> <h2 id="twig-️">Twig 🕸️</h2> <p>In Twig files, the empty line helps avoid unwanted characters in the generated HTML. Thanks to this, your page doesn’t look like a patchwork of random spaces and enters. And version control systems? They’re grateful too! 🙏</p> <h2 id="summary-">Summary 🎉</h2> <p>Adding an empty line at the end of PHP and Twig files is not just a technical necessity, but also a part of programming culture. It protects against errors, keeps things tidy, and makes tools happy. So next time you add an empty line, you can feel like a code hero! 🦸‍♂️</p>]]></content><author><name></name></author><category term="php"/><summary type="html"><![CDATA[This post explains why developers add an empty line at the end of PHP and Twig files, presenting technical, historical, and humorous reasons for this practice. You'll learn how it affects compatibility, code readability, and helps avoid errors.]]></summary></entry><entry><title type="html">🧹 How to Remove Recipes from the `extra` Section in composer.json</title><link href="https://ziumper.github.io/blog/2025/Composer-recipes-fix/" rel="alternate" type="text/html" title="🧹 How to Remove Recipes from the `extra` Section in composer.json"/><published>2025-09-22T00:00:00+00:00</published><updated>2025-09-22T00:00:00+00:00</updated><id>https://ziumper.github.io/blog/2025/Composer-recipes-fix</id><content type="html" xml:base="https://ziumper.github.io/blog/2025/Composer-recipes-fix/"><![CDATA[<p>When working with PHP projects, especially those using Symfony, you might encounter the <code class="language-plaintext highlighter-rouge">extra</code> section in your <code class="language-plaintext highlighter-rouge">composer.json</code>file. This section often contains recipes that help configure your project. However, over time, you may want to remove or clean up these recipes.</p> <h2 id="what-is-the-extra-section-in-composerjson">What is the <code class="language-plaintext highlighter-rouge">extra</code> Section in composer.json?</h2> <p>The <code class="language-plaintext highlighter-rouge">extra</code> section is a special place in your <code class="language-plaintext highlighter-rouge">composer.json</code> file for custom configuration.<br/> It is used by Composer plugins, frameworks (like Symfony), and scripts to store additional settings.</p> <h3 id="example">Example</h3> <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"extra"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"symfony"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"allow-contrib"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
      </span><span class="nl">"recipes"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"symfony/console"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1.0"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"ref"</span><span class="p">:</span><span class="w"> </span><span class="s2">"abcdef"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div> <h2 id="for-what-i-used-the-extra-section">For what I used the <code class="language-plaintext highlighter-rouge">extra</code> section?</h2> <p>Well I used it to store Symfony Flex recipes. Symfony Flex is a Composer plugin that helps manage Symfony applications by automating the installation and configuration of packages. When you install a package that has a recipe, Symfony Flex adds configuration files and settings to your project automatically. You may prefer to remove some of these recipes if they are no longer needed or if you want to customize your setup. In my case it was docker setup that I didn’t need.</p> <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"extra"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"symfony"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"allow-contrib"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
      </span><span class="nl">"docker"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div> <h2 id="symfony-flex">Symfony flex</h2> <p>The Flex recipes make a few assumptions about your project’s directory structure. Some of these assumptions can be customized by adding a key under the extra section of your composer.json file. For example, to tell Flex to copy any PHP classes into src/App instead of src:</p> <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"..."</span><span class="p">:</span><span class="w"> </span><span class="s2">"..."</span><span class="p">,</span><span class="w">

  </span><span class="nl">"extra"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"src-dir"</span><span class="p">:</span><span class="w"> </span><span class="s2">"src/App"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div> <h2 id="troubleshooting">Troubleshooting</h2> <p>If you encounter issues after removing recipes, try the following steps:</p> <ul> <li>Run <code class="language-plaintext highlighter-rouge">composer install</code> to refresh your dependencies.</li> <li>Check for missing configuration files or environment variables.</li> <li>Review Symfony documentation for any manual steps required after recipe removal.</li> </ul> <h2 id="additional-resources">Additional Resources</h2> <ul> <li><a href="https://symfony.com/doc/current/setup/flex.html">Symfony Recipes Documentation</a></li> <li><a href="https://getcomposer.org/doc/04-schema.md#extra">Composer Extra Section</a></li> </ul>]]></content><author><name></name></author><category term="php"/><category term="composer"/><category term="symfony"/><category term="symfony-flex"/><summary type="html"><![CDATA[A quick guide to understanding and cleaning up the `extra` section in composer.json, especially Symfony recipes.]]></summary></entry><entry><title type="html">🧐 Floats and the Mystery of -0.0 PHP Review #5</title><link href="https://ziumper.github.io/blog/2025/Floats-and-mystery-of-zero/" rel="alternate" type="text/html" title="🧐 Floats and the Mystery of -0.0 PHP Review #5"/><published>2025-09-18T00:00:00+00:00</published><updated>2025-09-18T00:00:00+00:00</updated><id>https://ziumper.github.io/blog/2025/Floats-and-mystery-of-zero</id><content type="html" xml:base="https://ziumper.github.io/blog/2025/Floats-and-mystery-of-zero/"><![CDATA[<p>Yes, really. <code class="language-plaintext highlighter-rouge">-0.0</code> and <code class="language-plaintext highlighter-rouge">0.0</code> are two different byte representations according to the <a href="https://en.wikipedia.org/wiki/IEEE_754">IEEE 754 standard</a> – but in practice… PHP treats them as the same.</p> <p>I had this code:</p> <div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">function</span> <span class="n">getPercentage</span><span class="p">():</span> <span class="kt">int</span>
<span class="p">{</span>
    <span class="c1">// epsilon to avoid division by zero</span>
    <span class="k">if</span> <span class="p">(</span><span class="nb">abs</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="n">limitPrice</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">1</span><span class="n">e</span><span class="o">-</span><span class="mi">8</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="nb">min</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="p">(</span><span class="n">int</span><span class="p">)</span> <span class="nb">round</span><span class="p">((</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="n">totalCartPrice</span> <span class="o">/</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="n">limitPrice</span><span class="p">)</span> <span class="o">*</span> <span class="mi">100</span><span class="p">));</span>
<span class="p">}</span>
</code></pre></div></div> <p>And I started wondering:</p> <ul> <li>Can I just compare <code class="language-plaintext highlighter-rouge">$this-&gt;limitPrice == 0.0</code>?</li> <li>What if <code class="language-plaintext highlighter-rouge">limitPrice</code> = <code class="language-plaintext highlighter-rouge">-0.0</code>? 🤯</li> </ul> <hr/> <h2 id="-analysis">🔬 Analysis</h2> <h3 id="1️⃣-ieee-754-and--00">1️⃣ IEEE 754 and -0.0</h3> <p>In the floating point standard, there is <strong>positive zero (0.0)</strong> and <strong>negative zero (-0.0)</strong>.<br/> At the byte level, they are two different values:</p> <ul> <li><code class="language-plaintext highlighter-rouge">0.0</code> → <code class="language-plaintext highlighter-rouge">0x0000000000000000</code></li> <li><code class="language-plaintext highlighter-rouge">-0.0</code> → <code class="language-plaintext highlighter-rouge">0x8000000000000000</code></li> </ul> <p>So yes, binary-wise, they are not the same.</p> <hr/> <h3 id="2️⃣-how-php-handles-it">2️⃣ How PHP Handles It</h3> <p>Compare this:</p> <div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">var_dump</span><span class="p">(</span><span class="mf">0.0</span> <span class="o">==</span> <span class="o">-</span><span class="mf">0.0</span><span class="p">);</span>   <span class="c1">// true</span>
<span class="nb">var_dump</span><span class="p">(</span><span class="mf">0.0</span> <span class="o">===</span> <span class="o">-</span><span class="mf">0.0</span><span class="p">);</span>  <span class="c1">// true</span>
</code></pre></div></div> <p>✅ PHP treats these two values as equal both with loose (<code class="language-plaintext highlighter-rouge">==</code>) and strict (<code class="language-plaintext highlighter-rouge">===</code>) comparison.</p> <p>But watch this:</p> <div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">var_dump</span><span class="p">(</span><span class="mi">1</span> <span class="o">/</span> <span class="mf">0.0</span><span class="p">);</span>  <span class="c1">// float(INF)</span>
<span class="nb">var_dump</span><span class="p">(</span><span class="mi">1</span> <span class="o">/</span> <span class="o">-</span><span class="mf">0.0</span><span class="p">);</span> <span class="c1">// float(-INF)</span>
</code></pre></div></div> <p>➡️ The difference shows up when dividing – the sign of zero affects the result (<code class="language-plaintext highlighter-rouge">+∞</code> vs <code class="language-plaintext highlighter-rouge">-∞</code>).</p> <hr/> <h3 id="3️⃣-does-it-matter-in-my-case">3️⃣ Does It Matter in My Case?</h3> <p>In my code, <code class="language-plaintext highlighter-rouge">limitPrice</code> has <strong>at most 4 decimal places</strong>, comes from the database or simple operations.<br/> I don’t have strange negative zeros coming from complex math.<br/> So I can safely write:</p> <div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="n">limitPrice</span> <span class="o">==</span> <span class="mf">0.0</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div> <p>And everything works just fine.<br/> Epsilon (<code class="language-plaintext highlighter-rouge">1e-8</code>) is not needed here, because I don’t have rounding errors at the 17th decimal place.</p> <hr/> <h2 id="-bonus-how-to-detect--00-for-the-nerds">🛠 Bonus: How to Detect -0.0 (For the Nerds)</h2> <p>If for some reason you <strong>must know</strong> whether it’s -0.0, here’s the trick:</p> <div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">function</span> <span class="n">isNegativeZero</span><span class="p">(</span><span class="kt">float</span> <span class="nv">$x</span><span class="p">):</span> <span class="kt">bool</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nv">$x</span> <span class="o">===</span> <span class="mf">0.0</span> <span class="o">&amp;&amp;</span> <span class="mi">1</span> <span class="o">/</span> <span class="nv">$x</span> <span class="o">===</span> <span class="o">-</span><span class="no">INF</span><span class="p">;</span>
<span class="p">}</span>

<span class="nb">var_dump</span><span class="p">(</span><span class="nf">isNegativeZero</span><span class="p">(</span><span class="mf">0.0</span><span class="p">));</span>  <span class="c1">// false</span>
<span class="nb">var_dump</span><span class="p">(</span><span class="nf">isNegativeZero</span><span class="p">(</span><span class="o">-</span><span class="mf">0.0</span><span class="p">));</span> <span class="c1">// true</span>
</code></pre></div></div> <p>Yes, you actually divide by zero to detect it. 🧪</p> <hr/> <h2 id="-conclusion">✅ Conclusion</h2> <ul> <li>Yes, -0.0 and 0.0 are different bytes.</li> <li>PHP treats them as equal (<code class="language-plaintext highlighter-rouge">==</code>, <code class="language-plaintext highlighter-rouge">===</code>).</li> <li>You can safely write <code class="language-plaintext highlighter-rouge">== 0.0</code> in comparisons.</li> <li>Only division by 0.0 vs -0.0 returns different infinities (INF vs -INF) – so when dividing, you may want to use <code class="language-plaintext highlighter-rouge">abs()</code>.</li> </ul> <hr/> <h2 id="-summary">🎉 Summary</h2> <p>I managed to simplify my code and <strong>instead of epsilons</strong> I just have:</p> <div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">function</span> <span class="n">getPercentage</span><span class="p">():</span> <span class="kt">int</span>
<span class="p">{</span>
    <span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="n">limitPrice</span> <span class="o">?</span> <span class="nb">min</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="p">(</span><span class="n">int</span><span class="p">)</span> <span class="nb">round</span><span class="p">((</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="n">totalCartPrice</span> <span class="o">/</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="n">limitPrice</span><span class="p">)</span> <span class="o">*</span> <span class="mi">100</span><span class="p">))</span> <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div> <p>Cleaner, simpler, and still safe.<br/> And the fact that -0.0 exists – well, that’s a great trivial fact to impress your fellow developers over coffee. ☕️😎</p> <p>PS: If you need an epsilon, it’s better to use this constant:</p> <div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kc">PHP_FLOAT_EPSILON</span>
</code></pre></div></div>]]></content><author><name></name></author><category term="php"/><summary type="html"><![CDATA[Did you know that PHP has negative zero? This post is a journey through the world of negative zeros, division by infinity, and why in your code you can still safely write `== 0.0`.]]></summary></entry><entry><title type="html">PhpUnit willReturnMap - PHP Review #4</title><link href="https://ziumper.github.io/blog/2025/PHPUnit-will-return-map-trap/" rel="alternate" type="text/html" title="PhpUnit willReturnMap - PHP Review #4"/><published>2025-09-17T00:00:00+00:00</published><updated>2025-09-17T00:00:00+00:00</updated><id>https://ziumper.github.io/blog/2025/PHPUnit-will-return-map-trap</id><content type="html" xml:base="https://ziumper.github.io/blog/2025/PHPUnit-will-return-map-trap/"><![CDATA[<p><strong>Description:</strong><br/> A story about how a single little <code class="language-plaintext highlighter-rouge">null</code> cost me 30 minutes of debugging tests,<br/> and then I found the truth in the documentation… which is completely silent about it. 🙃</p> <hr/> <h2 id="-the-problem">🧐 The Problem</h2> <p>Let’s assume we have a method like this:</p> <div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">MyService</span> <span class="p">{</span>
    <span class="k">public</span> <span class="k">function</span> <span class="n">doStuff</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$key</span><span class="p">,</span> <span class="kt">?string</span> <span class="nv">$flag</span> <span class="o">=</span> <span class="kc">null</span><span class="p">):</span> <span class="kt">string</span> <span class="p">{</span>
        <span class="k">return</span> <span class="s2">"Result: "</span> <span class="mf">.</span> <span class="nv">$key</span> <span class="mf">.</span> <span class="p">(</span><span class="nv">$flag</span> <span class="o">?</span> <span class="s2">" (</span><span class="nv">$flag</span><span class="s2">)"</span> <span class="o">:</span> <span class="s2">""</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div> <p>And you want to test a class that uses <code class="language-plaintext highlighter-rouge">MyService</code> – of course, with a mock.<br/> You use <strong><code class="language-plaintext highlighter-rouge">willReturnMap</code></strong>, because it’s elegant and works for multiple sets of arguments.</p> <p>Sounds simple?<br/> Let’s look at the test:</p> <div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$mock</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">createMock</span><span class="p">(</span><span class="nc">MyService</span><span class="o">::</span><span class="n">class</span><span class="p">);</span>

<span class="nv">$mock</span><span class="o">-&gt;</span><span class="nf">method</span><span class="p">(</span><span class="s1">'doStuff'</span><span class="p">)</span>
    <span class="o">-&gt;</span><span class="nf">willReturnMap</span><span class="p">([</span>
        <span class="p">[</span><span class="s1">'foo'</span><span class="p">,</span> <span class="s1">'Result: foo (bar)'</span><span class="p">],</span>
        <span class="p">[</span><span class="s1">'baz'</span><span class="p">,</span> <span class="s1">'Result: baz'</span><span class="p">],</span>
    <span class="p">]);</span>
</code></pre></div></div> <p>Looks good, right?<br/> <strong>NOPE.</strong> 😅</p> <hr/> <h2 id="-what-happens">💥 What happens?</h2> <p>If you don’t pass exactly as many arguments as the method takes, PHPUnit will say:</p> <blockquote> <p>“I have no idea what you mean” 🤷‍♂️<br/> and return <code class="language-plaintext highlighter-rouge">null</code>.</p> </blockquote> <p>So when your production code calls:</p> <div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$service</span><span class="o">-&gt;</span><span class="nf">doStuff</span><span class="p">(</span><span class="s1">'baz'</span><span class="p">);</span>
</code></pre></div></div> <p>the mock will say:</p> <blockquote> <p>“Sorry, I don’t know that map.”</p> </blockquote> <p>And your test explodes. 💣</p> <hr/> <h2 id="-why-does-this-happen">🤯 Why does this happen?</h2> <p>Because <code class="language-plaintext highlighter-rouge">willReturnMap</code> matches arguments <strong>by position</strong>, exactly 1:1.<br/> A default <code class="language-plaintext highlighter-rouge">null</code> argument isn’t magically “guessed”.<br/> You need to explicitly put it in the array.</p> <hr/> <h2 id="-the-correct-solution">✅ The Correct Solution</h2> <p>You need to <strong>match the exact number of arguments</strong>, even if they’re default:</p> <div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$mock</span><span class="o">-&gt;</span><span class="nf">method</span><span class="p">(</span><span class="s1">'doStuff'</span><span class="p">)</span>
    <span class="o">-&gt;</span><span class="nf">willReturnMap</span><span class="p">([</span>
        <span class="p">[</span><span class="s1">'foo'</span><span class="p">,</span> <span class="s1">'bar'</span><span class="p">,</span> <span class="s1">'Result: foo (bar)'</span><span class="p">],</span>
        <span class="p">[</span><span class="s1">'baz'</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="s1">'Result: baz'</span><span class="p">],</span> <span class="c1">// &lt;--- null is required!</span>
    <span class="p">]);</span>
</code></pre></div></div> <p>Yeah, I know – seems obvious, but sometimes you hope for a bit of magic. 🪄<br/> Sadly – PHPUnit offers no magic here.</p> <hr/> <h2 id="-the-moral-of-the-story">📝 The Moral of the Story</h2> <ul> <li><code class="language-plaintext highlighter-rouge">willReturnMap</code> is great, but it’s <strong>very literal</strong>.</li> <li>If the method takes 2 arguments – your map must have 2 arguments.</li> <li>Even if the second argument defaults to <code class="language-plaintext highlighter-rouge">null</code>.</li> <li>PHPUnit isn’t a fortune teller – it won’t guess you meant the default. 😅</li> </ul> <hr/> <h2 id="-takeaways">🎉 Takeaways</h2> <p>After this discovery, my tests stopped exploding.<br/> Knowing that <strong>you have to explicitly provide every argument in the map</strong> saved me hours of debugging.<br/> Now I know – with mocks, it’s better to provide one argument too many than one too few.</p> <blockquote> <p>🧠 <strong>Key Point:</strong> <code class="language-plaintext highlighter-rouge">willReturnMap</code> ≠ flexible matching.<br/> It’s a <strong>strict argument map</strong> – provide everything or don’t complain when your tests burn. 🔥</p> </blockquote> <hr/> <p>✍️ <strong>PS:</strong> If anyone finds a mention of this case in the PHPUnit docs – let me know.<br/> I couldn’t find it, and I searched half the internet. 🙈</p> <p><a href="https://docs.phpunit.de/en/12.3/test-doubles.html#willreturnmap">Link to documentation</a></p>]]></content><author><name></name></author><category term="php"/><summary type="html"><![CDATA[🐛 "PhpUnit, willReturnMap and default arguments – how to fall into a subtle trap 🪤"]]></summary></entry></feed>