<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Alexandru Nastase</title>
        <link>https://alexandrunastase.com</link>
        <description>Alexandru Nastase's blog</description>
        <lastBuildDate>Fri, 06 Mar 2026 09:02:12 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Alexandru Nastase</title>
            <url>https://alexandrunastase.com/favicon.ico</url>
            <link>https://alexandrunastase.com</link>
        </image>
        <copyright>All rights reserved 2026</copyright>
        <item>
            <title><![CDATA[Editing Linux kernel parameters with kernelstub]]></title>
            <link>https://alexandrunastase.com/posts/editing-linux-kernel-parameters-with-kernelstub</link>
            <guid>https://alexandrunastase.com/posts/editing-linux-kernel-parameters-with-kernelstub</guid>
            <pubDate>Tue, 14 Jan 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Learn how to safely edit Linux kernel parameters using kernelstub. Fix AMD GPU issues, manage EFI boot configs, and troubleshoot hardware problems on Ubuntu/Pop!_OS.]]></description>
            <content:encoded><![CDATA[<p>I recently played the gamble of getting a very new processor for my new work laptop, the AMD Ryzen™ AI 9 HX 370. Even the drivers for Windows seem problematic so I expected there could be some minor hiccups for Linux as well. Luckily the only issue I encountered was one related to the GPU rendering that could be fixed temporarily by updating a kernel parameter. I&#x27;m mainly writing this to have a future reference for this kind of change and document some of the things I found out along the way.</p>
<h3>Why kernelstub is the safer choice</h3>
<p>The simplest way I found to update kernel parameters is by using <code>kernelstub</code>. I remember doing this some years ago without it and I remember the process being a lot more unclear and prone to error, and especially when it comes to this kind of sensitive things I prefer to be on the safe side.</p>
<p>Unlike manually editing boot configurations or directly modifying EFI entries, kernelstub provides a more reliable interface that handles the complexities of modern UEFI boot systems. It automatically updates the correct boot entries and maintains consistency across different boot scenarios.</p>
<h3>Basic kernelstub commands</h3>
<p>Before making any changes, it&#x27;s always good practice to use the <code>--dry-run</code> parameter to see what kernelstub would do without actually applying the changes:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> kernelstub -a <span class="token string">&quot;parameter_name=value&quot;</span> --dry-run
</code></pre>
<p>To add a kernel parameter you can use:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> kernelstub -a <span class="token string">&quot;parameter_name=value&quot;</span>
</code></pre>
<p>For example, to add the AMD GPU debug mask parameter that fixed my rendering issues:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> kernelstub -a <span class="token string">&quot;amdgpu.dcdebugmask=0x12&quot;</span>
</code></pre>
<p>You can verify changes by checking the current configuration:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> kernelstub -p
</code></pre>
<p>Alternatively, you can directly check the boot configuration file:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">cat</span> /boot/efi/loader/entries/Pop_OS-current.conf
</code></pre>
<p>To remove a kernel parameter use:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> kernelstub -d <span class="token string">&quot;parameter_name=value&quot;</span>
</code></pre>
<p>After making changes, reboot and test that your system behaves as expected.</p>
<h3>Tested using</h3>
<ul>
<li>Pop!_OS 22.04 LTS</li>
<li>Kernel version: 6.12.10</li>
<li>AMD Ryzen™ AI 9 HX 370 processor</li>
<li>kernelstub version: 3.1.4</li>
</ul>
<h3>Links</h3>
<ul>
<li><a href="https://github.com/isantop/kernelstub">kernelstub GitHub repository</a></li>
<li><a href="https://www.kernel.org/doc/html/latest/admin-guide/kernel-parameters.html">Kernel parameters documentation</a></li>
<li><a href="https://wiki.archlinux.org/title/AMDGPU">AMD GPU kernel parameters</a></li>
</ul>
<p>If this guide becomes outdated or if you have any issues with any of the steps, feel free to reach out.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The joy of implementing Strategy Pattern in Symfony]]></title>
            <link>https://alexandrunastase.com/posts/the-joy-of-implementing-strategy-pattern-in-symfony</link>
            <guid>https://alexandrunastase.com/posts/the-joy-of-implementing-strategy-pattern-in-symfony</guid>
            <pubDate>Mon, 04 Dec 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[The joy of implementing Strategy Pattern in Symfony]]></description>
            <content:encoded><![CDATA[<p>One of the design patterns that I find myself most often implementing is the Strategy Design pattern. Oftentimes it helps decouple logic and allows better separation of concerns.</p>
<p>In previous versions of Symfony, it was of course possible to implement the pattern, but there was a bit more friction involved, like the need to fiddle with yaml files. This has dissapeared meanwhile thanks to the introduction of the attributes in PHP8. Now that we have the <code>TaggedIterator</code> and <code>AutoconfigureTag</code> attributes at our disposal, it has never been so effortless to implement the strategy pattern using Symfony framework.</p>
<p>In this post, we&#x27;ll go through a concrete example of how to do this, in which we&#x27;ll highlight the main components.</p>
<h2>Case Study</h2>
<p>Let&#x27;s suppose we&#x27;re building an imaginary Payment Gateway Software that supports some of the major credit/debit card providers (Visa, Mastercard, and American Express).</p>
<p>To keep things simple we will ignore lots of important elements like the credit card number, CVV, and so on and of course, there won&#x27;t be any requests to external entities.</p>
<p><strong>Acceptance criteria:</strong></p>
<ul>
<li>Create a POST endpoint called <code>/card-payments</code> that accepts the following JSON payload:</li>
</ul>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
  <span class="token property">&quot;amount&quot;</span><span class="token operator">:</span> <span class="token string">&quot;12.22&quot;</span><span class="token punctuation">,</span>
  <span class="token property">&quot;currency&quot;</span><span class="token operator">:</span> <span class="token string">&quot;EUR&quot;</span><span class="token punctuation">,</span>
  <span class="token property">&quot;provider&quot;</span><span class="token operator">:</span> <span class="token string">&quot;visa|mastercard|amex&quot;</span>
<span class="token punctuation">}</span>
</code></pre>
<ul>
<li>The endpoint should return a JSON response containing a <code>transactionId</code> property that has as a prefix the name of the payment provider passed in the input and is followed by some &quot;random&quot; string similar to the example below:</li>
</ul>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
 <span class="token property">&quot;transactionId&quot;</span><span class="token operator">:</span> <span class="token string">&quot;visa_asd22kjlkoiposd&quot;</span>
<span class="token punctuation">}</span>
</code></pre>
<ul>
<li>The assumption is that other logic should be dealt for each provider so generating the transactionId should be separated for each card payment provider</li>
</ul>
<h2>An example implementation</h2>
<p>Note, that a lot of important aspects like validation, exception handling, and code structuring have not been considered in detail as the focus is on the strategy pattern itself.</p>
<p>First we&#x27;ll start out by building the <code>CardPaymentController</code>, where we get the payload, deserialize it to a <code>CardPaymentDto</code>, and pass it further to a <code>CardPaymentService</code> which is responsible for finding the correct strategy and retrieving the transactionId from the strategy.</p>
<pre class="language-php"><code class="language-php"><span class="token operator">...</span>
<span class="token keyword">class</span> <span class="token class-name-definition class-name">CardPaymentController</span>
<span class="token punctuation">{</span>
   <span class="token operator">...</span>
    <span class="token attribute"><span class="token delimiter punctuation">#[</span><span class="token attribute-content"><span class="token attribute-class-name class-name">Route</span><span class="token punctuation">(</span><span class="token string single-quoted-string">&#x27;/card-payments&#x27;</span><span class="token punctuation">,</span> <span class="token attribute-class-name class-name">methods</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">&#x27;POST&#x27;</span><span class="token punctuation">]</span><span class="token punctuation">)</span></span><span class="token delimiter punctuation">]</span></span>
    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">index</span><span class="token punctuation">(</span><span class="token class-name type-declaration">Request</span> <span class="token variable">$request</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token class-name return-type">Response</span>
    <span class="token punctuation">{</span>
        <span class="token variable">$paymentDto</span> <span class="token operator">=</span> <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">serializer</span><span class="token operator">-&gt;</span><span class="token function">deserialize</span><span class="token punctuation">(</span><span class="token variable">$request</span><span class="token operator">-&gt;</span><span class="token function">getContent</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token scope">CardPaymentDto<span class="token punctuation">::</span></span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">&#x27;json&#x27;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token variable">$paymentResponse</span> <span class="token operator">=</span> <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">cardPaymentService</span><span class="token operator">-&gt;</span><span class="token function">handleCardPayment</span><span class="token punctuation">(</span><span class="token variable">$paymentDto</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">JsonResponse</span><span class="token punctuation">(</span><span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">serializer</span><span class="token operator">-&gt;</span><span class="token function">serialize</span><span class="token punctuation">(</span><span class="token variable">$paymentResponse</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">&#x27;json&#x27;</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token argument-name">json</span><span class="token punctuation">:</span> <span class="token constant boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>After the controller is created we will implement the strategy interface:</p>
<pre class="language-php"><code class="language-php"><span class="token attribute"><span class="token delimiter punctuation">#[</span><span class="token attribute-content"><span class="token attribute-class-name class-name">AutoconfigureTag</span><span class="token punctuation">(</span><span class="token attribute-class-name class-name">CardPaymentStrategyInterface</span><span class="token operator">::</span><span class="token constant">class</span><span class="token punctuation">)</span></span><span class="token delimiter punctuation">]</span></span>
<span class="token keyword">interface</span> <span class="token class-name-definition class-name">CardPaymentStrategyInterface</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">isPaymentProviderSupported</span><span class="token punctuation">(</span><span class="token keyword type-hint">string</span> <span class="token variable">$paymentProvider</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">handlePayment</span><span class="token punctuation">(</span><span class="token class-name type-declaration">CardPaymentDto</span> <span class="token variable">$payment</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token class-name return-type">CardPaymentResponseDto</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Note the use of the <code>AutoconfigureTag</code> attribute here. Usually, it&#x27;s more common to use a string here as the service tag, but I prefer using the name of the interface to minimize the chance of typos.</p>
<p>As the interface is defined we can start creating the strategy implementations:</p>
<pre class="language-php"><code class="language-php"><span class="token keyword">class</span> <span class="token class-name-definition class-name">VisaCardPaymentStrategy</span> <span class="token keyword">implements</span> <span class="token class-name">CardPaymentStrategyInterface</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">isPaymentProviderSupported</span><span class="token punctuation">(</span><span class="token keyword type-hint">string</span> <span class="token variable">$paymentProvider</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword return-type">bool</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token variable">$paymentProvider</span> <span class="token operator">===</span> <span class="token scope">PaymentProvider<span class="token punctuation">::</span></span><span class="token constant">VISA</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">handlePayment</span><span class="token punctuation">(</span><span class="token class-name type-declaration">CardPaymentDto</span> <span class="token variable">$payment</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token class-name return-type">CardPaymentResponseDto</span>
    <span class="token punctuation">{</span>
        <span class="token comment">// Call Visa payment provider</span>
        <span class="token comment">// More logic</span>

        <span class="token variable">$transactionId</span> <span class="token operator">=</span> <span class="token scope">PaymentProvider<span class="token punctuation">::</span></span><span class="token constant">VISA</span> <span class="token operator">.</span> <span class="token function">uniqid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">CardPaymentResponseDto</span><span class="token punctuation">(</span><span class="token variable">$transactionId</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>and similar for the other payment providers:</p>
<pre class="language-php"><code class="language-php"><span class="token keyword">class</span> <span class="token class-name-definition class-name">MastercardCardPaymentStrategy</span> <span class="token keyword">implements</span> <span class="token class-name">CardPaymentStrategyInterface</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">isPaymentProviderSupported</span><span class="token punctuation">(</span><span class="token keyword type-hint">string</span> <span class="token variable">$paymentProvider</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword return-type">bool</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token variable">$paymentProvider</span> <span class="token operator">===</span> <span class="token scope">PaymentProvider<span class="token punctuation">::</span></span><span class="token constant">MASTERCARD</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">handlePayment</span><span class="token punctuation">(</span><span class="token class-name type-declaration">CardPaymentDto</span> <span class="token variable">$payment</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token class-name return-type">CardPaymentResponseDto</span>
    <span class="token punctuation">{</span>
        <span class="token comment">// Call Mastercard payment provider</span>
        <span class="token comment">// More logic</span>

        <span class="token variable">$transactionId</span> <span class="token operator">=</span> <span class="token scope">PaymentProvider<span class="token punctuation">::</span></span><span class="token constant">MASTERCARD</span> <span class="token operator">.</span> <span class="token function">uniqid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">CardPaymentResponseDto</span><span class="token punctuation">(</span><span class="token variable">$transactionId</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>When the supported strategies are defined we can start implementing the CardPaymentService:</p>
<pre class="language-php"><code class="language-php"><span class="token keyword">class</span> <span class="token class-name-definition class-name">CardPaymentService</span> <span class="token keyword">implements</span> <span class="token class-name">CardPaymentServiceInterface</span>
<span class="token punctuation">{</span>
    <span class="token doc-comment comment">/**
     * <span class="token keyword">@param</span> <span class="token class-name">iterable</span>&lt;CardPaymentStrategyInterface&gt; $cardPaymentStrategies
     */</span>
    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">__construct</span><span class="token punctuation">(</span>
        <span class="token attribute"><span class="token delimiter punctuation">#[</span><span class="token attribute-content"><span class="token attribute-class-name class-name">TaggedIterator</span><span class="token punctuation">(</span><span class="token attribute-class-name class-name">CardPaymentStrategyInterface</span><span class="token operator">::</span><span class="token constant">class</span><span class="token punctuation">)</span></span><span class="token delimiter punctuation">]</span></span>
        <span class="token keyword">private</span> readonly <span class="token keyword type-declaration">iterable</span> <span class="token variable">$cardPaymentStrategies</span><span class="token punctuation">,</span>
    <span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">handleCardPayment</span><span class="token punctuation">(</span><span class="token class-name type-declaration">CardPaymentDto</span> <span class="token variable">$paymentDto</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token class-name return-type">CardPaymentResponseDto</span>
    <span class="token punctuation">{</span>
        <span class="token variable">$paymentStrategy</span> <span class="token operator">=</span> <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">getStrategy</span><span class="token punctuation">(</span><span class="token variable">$paymentDto</span><span class="token operator">-&gt;</span><span class="token function">getProvider</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> <span class="token variable">$paymentStrategy</span><span class="token operator">-&gt;</span><span class="token function">handlePayment</span><span class="token punctuation">(</span><span class="token variable">$paymentDto</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getStrategy</span><span class="token punctuation">(</span><span class="token keyword type-hint">string</span> <span class="token variable">$paymentProvider</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token class-name return-type">CardPaymentStrategyInterface</span>
    <span class="token punctuation">{</span>
        <span class="token variable">$pickedPaymentStrategy</span> <span class="token operator">=</span> <span class="token constant">null</span><span class="token punctuation">;</span>
        <span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">cardPaymentStrategies</span> <span class="token keyword">as</span> <span class="token variable">$paymentStrategy</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$paymentStrategy</span><span class="token operator">-&gt;</span><span class="token function">isPaymentProviderSupported</span><span class="token punctuation">(</span><span class="token variable">$paymentProvider</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
                <span class="token variable">$pickedPaymentStrategy</span> <span class="token operator">=</span> <span class="token variable">$paymentStrategy</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">is_null</span><span class="token punctuation">(</span><span class="token variable">$pickedPaymentStrategy</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">throw</span> <span class="token scope">UnsupportedPaymentStrategyException<span class="token punctuation">::</span></span><span class="token function">createForPaymentProvider</span><span class="token punctuation">(</span><span class="token variable">$paymentProvider</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token keyword">return</span> <span class="token variable">$pickedPaymentStrategy</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Note the <code>TaggedIterator</code> attribute here. It allows us to collect all the classes that were auto-configured using the <code>CardPaymentStrategyInterface</code> and have them available as an iterable.
Here we simply find the correct strategy and call it using the <code>PaymentDto</code> and return the result of the picked strategy, which should contain the transactionId.</p>
<p>Make sure that service autoconfiguration is enabled in order for this setup work properly.</p>
<h2>Conclusion</h2>
<p>Applying the strategy pattern is really straightforward using the latest versions of Symfony and PHP (Symfony 6.4 and PHP 8.2 in the given example).
Apart from keeping the code much more decoupled, it offers the benefit of keeping the code open to extension but closed to modifications. So for example, if we were to add other payment providers, we wouldn&#x27;t need to touch any of the existing logic thus avoiding the risk of breaking anything there.</p>
<p>To see the entire implementation feel free to check out the <a href="https://github.com/alexandrunastase/blog-appendix/tree/main/strategy-pattern">git repository</a>.
Additionally from the code shown above, the repository contains a docker-compose setup and some basic tests that you can play around with.</p>
<hr/>
<h3>References:</h3>
<ul>
<li><a href="https://symfony.com/doc/6.4/service_container/tags.html">https://symfony.com/doc/6.4/service_container/tags.html</a></li>
<li><a href="https://symfony.com/doc/current/service_container.html#the-autoconfigure-option">https://symfony.com/doc/current/service_container.html#the-autoconfigure-option</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Configuring deprecation warnings when running tests in Symfony]]></title>
            <link>https://alexandrunastase.com/posts/most-common-symfony-deprecations-helper-options</link>
            <guid>https://alexandrunastase.com/posts/most-common-symfony-deprecations-helper-options</guid>
            <pubDate>Mon, 27 Nov 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h2>Some background</h2>
<p>If you&#x27;re using Symfony for a while, you&#x27;re probably aware of the <code>SYMFONY_DEPRECATIONS_HELPER</code> environment variable that you can set in phpunit.xml config. The variable is part
of the <a href="https://packagist.org/packages/symfony/phpunit-bridge">https://packagist.org/packages/symfony/phpunit-bridge</a> and comes with most default Symfony PHP Framework installs.</p>
<p>I personally found the naming of the options not very intuitive, and until I played around to figure out the behavior, I didn&#x27;t really get how they were supposed to be working.</p>
<h2>The options</h2>
<ul>
<li>max[total]</li>
<li>max[self]</li>
<li>max[direct]</li>
<li>max[indirect]</li>
<li>verbose</li>
<li>ignoreFile</li>
<li>logFile</li>
<li>quiet</li>
</ul>
<h3>max</h3>
<p>The <code>max</code> option is used to set a threshold on the number of deprecation notices, either based on their type (direct, indirect) or total number, after which the command line returns an error exit code.</p>
<p>There are several types of suboptions for max:</p>
<ul>
<li><code>max[total]</code> - sets a threshold on the total number of deprecations, both direct and indirect</li>
<li><code>max[direct]</code> - puts a threshold on deprecations that resulted directly from <em>your code</em></li>
<li><code>max[indirect]</code> - sets a threshold for on the number of deprecations caused by dependencies</li>
<li><code>max[self]</code> - used for libraries that use the depreaction system themselves and cannot afford to use any of the other configurations from above</li>
</ul>
<pre class="language-shell"><code class="language-shell"><span class="token assign-left variable">SYMFONY_DEPRECATIONS_HELPER</span><span class="token operator">=</span><span class="token string">&quot;max[total]=100&quot;</span> ./vendor/bin/phpunit
</code></pre>
<h3>vebose</h3>
<p>The <code>verbose</code> option is used to get more details about the exact files in which the depreaction notices were triggered. It&#x27;s often used in a url encoded manner with other options:</p>
<pre class="language-shell"><code class="language-shell"><span class="token assign-left variable">SYMFONY_DEPRECATIONS_HELPER</span><span class="token operator">=</span><span class="token string">&quot;max[total]=100&amp;verbose=1&quot;</span> ./vendor/bin/phpunit
</code></pre>
<h3>ignoreFile</h3>
<p>The <code>ignoreFile</code> option can be used to define inside a file a list of deprecation notices that should be ignored. <strong>Note that this feature was introduced starting with Symfony 6.1</strong></p>
<pre class="language-shell"><code class="language-shell"><span class="token assign-left variable">SYMFONY_DEPRECATIONS_HELPER</span><span class="token operator">=</span><span class="token string">&quot;ignoreFile=./tests/ignoreFile.txt&quot;</span> ./vendor/bin/phpunit
</code></pre>
<p>ignoreFile.txt :</p>
<pre class="language-txt"><code class="language-txt">`Serializer`
`EntityManager`
`Entity`
</code></pre>
<p>Basically each row represents a pattern of the files that should be ignored.</p>
<h3>generateBaseline &amp; baselineFile</h3>
<p>In order to create a baseline file you can use the <code>generateBaseline</code> and <code>baselineFile</code> options as follows:</p>
<pre class="language-shell"><code class="language-shell"><span class="token assign-left variable">SYMFONY_DEPRECATIONS_HELPER</span><span class="token operator">=</span><span class="token string">&#x27;generateBaseline=true&amp;baselineFile=./tests/baseline-file.json&#x27;</span> ./vendor/bin/phpunit
</code></pre>
<p>After the baseline file is generated you could run the tests without the generation option like:</p>
<pre class="language-shell"><code class="language-shell"><span class="token assign-left variable">SYMFONY_DEPRECATIONS_HELPER</span><span class="token operator">=</span><span class="token string">&#x27;baselineFile=./tests/baseline-file.json&#x27;</span> ./vendor/bin/phpunit
</code></pre>
<h3>logFile</h3>
<p>If you prefer turning off verbose output and write all of the deprecation notices in a file you can use the logFile option. Note that this option also hides the count of deprecation
notices at the end of the test run.</p>
<pre class="language-shell"><code class="language-shell"><span class="token assign-left variable">SYMFONY_DEPRECATIONS_HELPER</span><span class="token operator">=</span><span class="token string">&#x27;logFile=./var/log/deprecations.log&#x27;</span> ./vendor/bin/phpunit
</code></pre>
<h3>quiet</h3>
<p>The <code>quiet</code> option allows to change the verbosity more granularly than <code>vebose</code> and does so per type of deprecation. For example if you want to hide all
indirect deprecation notices you could use something like:</p>
<pre class="language-shell"><code class="language-shell"><span class="token assign-left variable">SYMFONY_DEPRECATIONS_HELPER</span><span class="token operator">=</span><span class="token string">&#x27;quiet[]=indirect&#x27;</span> ./vendor/bin/phpunit
</code></pre>
<p>One important thing to note about <code>quiet</code> is that, similarly to <code>vebose</code>, it just hides the details of the deprecations but doesn&#x27;t change the exist code of
the command in any way if the number of threshold deprecations was reached.</p>
<p>Unlike logFile, when using <code>quiet</code>, the count of deprecation notices is still shown.</p>
<h4>References:</h4>
<ul>
<li><a href="https://symfony.com/doc/current/components/phpunit_bridge.html">https://symfony.com/doc/current/components/phpunit_bridge.html</a></li>
<li><a href="https://github.com/symfony/phpunit-bridge">https://github.com/symfony/phpunit-bridge</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Shinto shrines and the longevity of software projects]]></title>
            <link>https://alexandrunastase.com/posts/shinto-shrines-and-the-longevity-of-software-projects</link>
            <guid>https://alexandrunastase.com/posts/shinto-shrines-and-the-longevity-of-software-projects</guid>
            <pubDate>Mon, 10 Jul 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>I stumbled upon this video while checking out letsencrypt in more detail.Found it randomly on their FAQ section on a question related to support : <a href="https://letsencrypt.org/docs/faq/">https://letsencrypt.org/docs/faq/</a>. Highly recommend you check it out :</p>
<div class="flex justify-center w-full h-0 max-w-full overflow-hidden "><iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/Xe1TZaElTAs" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"></iframe></div>
<p>Takeaway 1</p>
<ul>
<li>We have the tendency to be bad at evaluating the potential benefits of novel ways of doing things, like using community support instead of paid commercial support.
<blockquote>
<p>&quot;They didn&#x27;t care that it works in practice because they already knew it didn&#x27;t work in theory&quot;</p>
</blockquote>
</li>
</ul>
<p>Takeaway 2</p>
<ul>
<li>How much people in a certain community care about helping each other can be a better predictor for the longevity of a project than the business model</li>
</ul>
<p>Takeaway 3</p>
<ul>
<li>Since the internet emerged, there is a pattern of aggregating care into something long-lasting. Some well known examples are probably Linux and Wikipedia, but there are also some examples where people got together on platforms like MySpace or flickr.</li>
</ul>
<p>Since the video is from 2007, and I&#x27;m writing this in 2023, I wanted to check if the assumption still holds up. So while comp.lang.perl.misc newsgroup doesn&#x27;t seem that active, also because of the fall in popularity of Usenet, it does seem that the Perl community on <a href="https://www.reddit.com/r/perl/">reddit</a> is alive and kicking with over 16k members. Same applies for <a href="https://stackoverflow.com/questions/tagged/perl">stackoverflow</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to easily upgrade npm packages]]></title>
            <link>https://alexandrunastase.com/posts/how-to-update-npm-packages</link>
            <guid>https://alexandrunastase.com/posts/how-to-update-npm-packages</guid>
            <pubDate>Thu, 23 Feb 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h2>TLDR: How to update npm packages?</h2>
<blockquote>
<p>The simplest way is to install <code>npm-check-updates</code>, run <code>npx ncu</code>, followed by <code>npx ncu -u</code> to update the package.json followed by <code>npm install</code> to update packages in package.lock and node_modules.</p>
</blockquote>
<h2>Option 1: Classic npm approach</h2>
<p>npm comes with the tools to upgrade your packages out of the box. When running <code>npm outdated</code> you can get a list of packages that have available updates:</p>
<img alt="npm outdated result" srcSet="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fnpm_outdated_1.78652454.png&amp;w=750&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fnpm_outdated_1.78652454.png&amp;w=1920&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fnpm_outdated_1.78652454.png&amp;w=1920&amp;q=75" width="736" height="488" decoding="async" data-nimg="1" loading="lazy" style="color:transparent"/>
<p>We can update individual packages by running <code>npm update {package-name}</code>. Let&#x27;s try it for the last package on the list:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">npm</span> update sass
</code></pre>
<p>Now if we run <code>npm outdated</code> again we can (as seen in the image below) that the package was indeed updated. One thing to note is that while <code>package.lock</code> was updated <code>package.json</code> remains untouched.</p>
<img alt="npm outdated result after sass upgrade" srcSet="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fnpm_outdated_2.f65b0a8c.png&amp;w=750&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fnpm_outdated_2.f65b0a8c.png&amp;w=1920&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fnpm_outdated_2.f65b0a8c.png&amp;w=1920&amp;q=75" width="732" height="471" decoding="async" data-nimg="1" loading="lazy" style="color:transparent"/>
<p>Now we could do the same for all the packages and if you have a production-critical application, you probably want to pay close attention to the packages that you upgrade and the implications that an upgrade could have.</p>
<h2>Option 2: npm-check-updates approach</h2>
<p>Another option, that I find slightly more convenient, especially for more low-risk projects is using the <code>npm-check-updates</code> package. To install it simply run:</p>
<pre><code>npm install -g npm-check-updates
</code></pre>
<p>After it&#x27;s installed we can check for updates by running:</p>
<pre><code>npx ncu
</code></pre>
<p>Similar to <code>npm outdated</code> this gives us a list of all available updates:</p>
<img alt="npm-check-updates list of updates" srcSet="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fncu_1.a1d7a314.png&amp;w=750&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fncu_1.a1d7a314.png&amp;w=1920&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fncu_1.a1d7a314.png&amp;w=1920&amp;q=75" width="650" height="497" decoding="async" data-nimg="1" loading="lazy" style="color:transparent"/>
<p>In order to update one single package we can run:</p>
<pre class="language-bash"><code class="language-bash">npx ncu -uf sass
</code></pre>
<p>followed by:</p>
<pre><code>npm install
</code></pre>
<p>Now if we run <code>npx ncu</code> again we see the <code>sass</code> package was updated:</p>
<img alt="npm-check-updates with sass package updated" srcSet="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fncu_2.9649a3dd.png&amp;w=750&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fncu_2.9649a3dd.png&amp;w=1920&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fncu_2.9649a3dd.png&amp;w=1920&amp;q=75" width="681" height="465" decoding="async" data-nimg="1" loading="lazy" style="color:transparent"/>
<p>What is nice about the npm-check-updates package is that we can also update all of the packages if we choose so by running:</p>
<pre><code>npx ncu -u
</code></pre>
<p>followed again by</p>
<pre><code>npm install
</code></pre>
<p>Now if we run <code>npx ncu</code> again we get:</p>
<img alt="npm-check-updates result without any updates left" srcSet="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fncu_3.a4c7bec0.png&amp;w=640&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fncu_3.a4c7bec0.png&amp;w=1080&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fncu_3.a4c7bec0.png&amp;w=1080&amp;q=75" width="535" height="88" decoding="async" data-nimg="1" loading="lazy" style="color:transparent"/>
<p>Now both <code>package.json</code> and <code>package.lock</code> were updated, so this makes it clearer what version of the packages we have without the need to look into the <code>package.lock</code> file.</p>
<h2>Conclusion</h2>
<p>If you want to easily upgrade all your packages you can use the <code>npm-check-updates</code> npm package with the commands shown above, otherwise, you can also use npm&#x27;s built-in commands <code>npm outdated</code> and <code>npm update</code>.</p>
<p>References:</p>
<ul>
<li><a href="https://www.npmjs.com/package/npm-check-updates">https://www.npmjs.com/package/npm-check-updates</a></li>
<li><a href="https://docs.npmjs.com/cli/v7/commands">https://docs.npmjs.com/cli/v7/commands</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[What's the meaning of .PHONY in a Makefile?]]></title>
            <link>https://alexandrunastase.com/posts/meaning-of-phony-in-a-makefile</link>
            <guid>https://alexandrunastase.com/posts/meaning-of-phony-in-a-makefile</guid>
            <pubDate>Thu, 02 Feb 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h2>TLDR: What does .PHONY actually do?</h2>
<blockquote>
<p>.PHONY is used to mark a target as phony. That means a target that doesn&#x27;t take into consideration for execution any file that matches its name.</p>
</blockquote>
<p>Let&#x27;s say we have a target called <code>clear-cache</code>, which removes the cache of an application</p>
<pre class="language-makefile"><code class="language-Makefile"><span class="token target symbol">clear-cache</span><span class="token punctuation">:</span>
	rm -rf cache/*
</code></pre>
<p>Which results in the following command:</p>
<img srcSet="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fmakefile_phony_4.d28361aa.png&amp;w=640&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fmakefile_phony_4.d28361aa.png&amp;w=828&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fmakefile_phony_4.d28361aa.png&amp;w=828&amp;q=75" width="409" height="42" decoding="async" data-nimg="1" loading="lazy" style="color:transparent"/>
<p>The cache removal cli command will be run fine most of the time, but let&#x27;s check what happens if we <strong>add a file with the same name as the target</strong> at the same level as the Makefile</p>
<img srcSet="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fmakefile_phony_2.72fa3943.png&amp;w=640&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fmakefile_phony_2.72fa3943.png&amp;w=828&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fmakefile_phony_2.72fa3943.png&amp;w=828&amp;q=75" width="397" height="114" decoding="async" data-nimg="1" loading="lazy" style="color:transparent"/>
<p>Now when running make clear-cache again we get:</p>
<img srcSet="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fmakefile_phony_3.89b062db.png&amp;w=640&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fmakefile_phony_3.89b062db.png&amp;w=1080&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fmakefile_phony_3.89b062db.png&amp;w=1080&amp;q=75" width="471" height="52" decoding="async" data-nimg="1" loading="lazy" style="color:transparent"/>
<p>The Makefile says basically that it has the target file already and doesn&#x27;t need
to execute</p>
<p>This default behavior is not what we expect in this case and we want to override it. This is when the <strong>.PHONY</strong> directive comes to the rescue. Let&#x27;s update our example to use it</p>
<pre class="language-makefile"><code class="language-Makefile"><span class="token builtin-target builtin">.PHONY</span><span class="token punctuation">:</span> clear-cache
<span class="token target symbol">clear-cache</span><span class="token punctuation">:</span>
	rm -rf cache/*
</code></pre>
<p>Pro tip. Another way to mark the targets as phony is to have them all in place list rather than above each of them like:</p>
<pre class="language-makefile"><code class="language-Makefile"><span class="token target symbol">clear-cache</span><span class="token punctuation">:</span>
	rm -rf cache/*
<span class="token target symbol">clear-build</span><span class="token punctuation">:</span>
	rm -rf build/*

<span class="token builtin-target builtin">.PHONY</span><span class="token punctuation">:</span> clear-cache clear-build
</code></pre>
<p>After doing this, the command behaves as expected</p>
<img srcSet="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fmakefile_phony_4.d28361aa.png&amp;w=640&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fmakefile_phony_4.d28361aa.png&amp;w=828&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fmakefile_phony_4.d28361aa.png&amp;w=828&amp;q=75" width="409" height="42" decoding="async" data-nimg="1" loading="lazy" style="color:transparent"/>
<h3>Conclusion</h3>
<p>In order to override the default behavior in Makefile and use some targets like command runners you can use .PHONY.</p>
<p>References:</p>
<ul>
<li><a href="https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html">GNU Make Manual</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[On the importance of perspective]]></title>
            <link>https://alexandrunastase.com/posts/on-the-importance-of-perspective</link>
            <guid>https://alexandrunastase.com/posts/on-the-importance-of-perspective</guid>
            <pubDate>Fri, 09 Sep 2022 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Lately I had a realization about how important perspective is. There are two main ways to view a situation. In the first one, let&#x27;s call it <strong>Perspective 1</strong>, we can think about something that seems wrong that someone else does like “This is wrong, it shouldn&#x27;t be like this”, or &quot;This guy is an idiot. How can he do something like that?&quot;. When we do that we tend to affect our mood negatively and our wish to improve things fades away also. The other way we could see the situation, let&#x27;s call it <strong>Perspective 2</strong>, would be something like “What can I do, that is within my power in order to improve this situation, even if it’s just a tiny change for the better”. Many times our instinct nudges us towards Perspective 1 and this can lead to conflics or in the very least no improvement for the better and an increase in resentment. On the other hand if we go for Perspective 2, we can actually improve things and a number of gradual improvements like this can lead to a better overall situation.</p>
<p>One situation where choosing the right perspective is quite important is during <strong>Code Review</strong>. A lot of times people have different levels of knowledge and familiarity with certain code bases and there is the tendency to be some sort of a disconnect between people&#x27;s expectations of &quot;how things should be done&quot;. There is also different styles of Software Developers, some favoring more implementation and speed, while others favoring more detailed planning and questioning current abstractions.</p>
<p>Another case in which I noticed perspective can play an important role is in <strong>politics</strong>, and more exactly how people&#x27;s views of politics are. I think this can be observed from the political debates, but also from how the media covers the topics of the day. I&#x27;ve had the chance to live in two quite different societies (Romania and Germany) and what I saw is that when people tend to favor Perspective 2,  there is also a tendency towards more nuance in debates and going deeper in some topics, which can lead to better decisions, that are more in-line with what most of the population is thinking.</p>
<p>One thing that helped me nudge myself more towards Perspective 2 was the Stoic idea of the <strong>focusing on the things that you can control</strong>. Generally in life there are things that we can control and do something about and there are things that we have no power over. A lot of times we waste our time focusing on the things that we can&#x27;t control, becoming frustrated in the process, when we could instead just focus what is up to us without focusing on the rest.</p>
<p>So for example when it comes to code review, I can focus on understanding the problem, writing a clear comment and offering a good alternatives where I think improvements are needed, but expecting the other person to listen 100% to my suggestions is unrealistic and entirely out of my control, so I should probably skip that.</p>
<p>When it comes to politics, many have the tendency to expect the Goverment to fix all the problems, and when these expectations fall short, people tend to gravitate towards parties with more radical views, which usually don&#x27;t have real solutions and bring the state of affairs to an even worse state. What we could do instead would be to try to do all that is up to us for the concrete issues that we want fix. We can report the issues, we can call, email or go talk directly to people that can change things, but that&#x27;s where our task ends. That&#x27;s not to say we should be soft on the representatives and allow them to ignore our complaints, but having the unrealistic expectations of things getting fixed immediately doesn&#x27;t help anybody, neither ourselves, neither our family and friends that have to tolerate our bad mood.</p>
<p>I guess what I&#x27;m trying to say is that <strong>when giving feedback to other people, keep in mind what you can control and what you cannot</strong>. It&#x27;s healthier for everyone involved.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building a simple PHP dockerized environment]]></title>
            <link>https://alexandrunastase.com/posts/building-a-simple-php-dockerized-environment</link>
            <guid>https://alexandrunastase.com/posts/building-a-simple-php-dockerized-environment</guid>
            <pubDate>Sun, 08 May 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Unlike other ecosystems like .NET or Java in which containerizing an application for local development might seem more like a burden than a feature when it comes to PHP this is a necessity as well as a welcome bonus of mirroring the production environment very closely.]]></description>
            <content:encoded><![CDATA[<p>Unlike other ecosystems like .NET or Java in which containerizing an application for local development might seem more like a burden than a feature when it comes to PHP this is a necessity as well as a welcome bonus of mirroring the production environment very closely.</p>
<p>Some applications might require a PHP extension that is not present on the local system or there might be some specific php.ini configuration that is set on the production server, which is considered as part of the infrastructure rather than the application itself but can cause problems because of the inconsistency (I had this happen to me several years ago). Using Docker can solve these types of problems and has also the added benefit of being a really good way to develop applications, an argument for this being the good support for remote debugging in IDEs.</p>
<h3>Overview</h3>
<p>I will go through setting up the entire stack, that is PHP 8.1 with fpm, nginx and, mysql as the database and at the end, we will do basic Symfony installation to test everything together.</p>
<p>If you don&#x27;t just want to check out the repo with the entire setup you can find it on <a href="https://github.com/alexandrunastase/docker-php-devbox">Github</a></p>
<h3>Setting up docker-compose configuration</h3>
<p>There are two files that tell docker-compose what to spin up. The first is docker-compose.yml and the other is docker-compose.override.yml. The override file is responsible for exposing the host using the hostname <code>dockerhost</code>. This is useful for both debugging and for referencing other containers more easily.</p>
<pre><code>version: &#x27;3.5&#x27;

services:
  devbox:
    container_name: devbox-nginx
    build:
      context: ./docker/nginx
      dockerfile: Dockerfile
    ports:
      - &quot;9001:80&quot;
    volumes:
      - .:/app:cached
    restart: unless-stopped
    depends_on:
      - devbox-service

  devbox-service:
    container_name: devbox-service
    build:
      context: .
    volumes:
      - .:/app:cached
      - ./docker/service/php.ini:/usr/local/etc/php/conf.d/99-app.ini
      - ./docker/service/www.conf:/usr/local/etc/php-fpm.d/www.conf
    restart: unless-stopped
    environment:
      XDEBUG_CONFIG: ${XDEBUG_CONFIG}
      APP_ENV: ${APP_ENV}
      APP_DEBUG: ${APP_DEBUG}
      APP_SECRET: ${APP_SECRET}
    env_file:
      - .env
      - .env.local
    depends_on:
      - mysql

  mysql:
    image: mysql:8.0
    container_name: devbox-mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: database
    ports:
      - &quot;3308:3306&quot;
    volumes:
      - database-volume:/var/lib/mysql

volumes:
  database-volume:
    driver: &quot;local&quot;

</code></pre>
<h3>Adding the Dockerfiles</h3>
<p>There are two Dockerfiles used. One for php-fpm and the other for nginx. The one for the app itself is the php-fpm one and it&#x27;s found in the root of the project and the other one is found in docker/nginx/Dockerfile. The nginx Dockerfile just copies the nginx configuration defined in default.conf into the container. The nginx configuration is quite apart from the fact that the name of the service defined in docker-compose was specified</p>
<p>Dockerfile for nginx</p>
<pre><code>FROM nginx:stable

COPY default.conf /etc/nginx/conf.d/default.conf

</code></pre>
<p>Dockefile for php-fpm</p>
<pre><code>FROM php:8.1-fpm-alpine

LABEL maintainer=&quot;alexandrunastase@github&quot;
LABEL description=&quot;Devbox Docker image&quot;

# User build args
ARG APP_ENV=&quot;prod&quot;
ARG APP_DEBUG=&quot;0&quot;
ARG APP_LOG=&quot;php://stdout&quot;

# Environment variables
ENV APP_ENV=${APP_ENV}
ENV APP_DEBUG=${APP_DEBUG}
ENV APP_LOG=${APP_LOG}

ENV XDEBUG_CONFIG=&quot;&quot;
ENV COMPOSER_NO_INTERACTION=1

# Add PHP user
ARG PHP_USER_ID=1000
ARG PHP_GROUP_ID=1000
RUN set -x \
    &amp;&amp; addgroup -g $PHP_GROUP_ID -S php \
    &amp;&amp; adduser -u $PHP_USER_ID -D -S -G php php

# Install dependencies
RUN set -ex \
    &amp;&amp; docker-php-source extract \
    &amp;&amp; apk add --update --no-cache \
    ${PHPIZE_DEPS} \
    curl \
    # Runtime deps
    icu-dev icu-libs \
    libzip-dev zlib-dev \
    libxml2-dev \
    oniguruma-dev \
    &amp;&amp; pecl install xdebug \
    &amp;&amp; docker-php-ext-install intl opcache pdo_mysql zip bcmath mbstring sockets pcntl soap sockets ctype &gt; /dev/null \
    &amp;&amp; docker-php-ext-enable intl opcache pdo_mysql zip bcmath mbstring sockets pcntl soap sockets ctype \
    &amp;&amp; apk del ${PHPIZE_DEPS} \
    &amp;&amp; docker-php-source delete

# Copy configuration files
COPY ./docker/service/www.conf /usr/local/etc/php-fpm.d/www.conf
COPY ./docker/service/php.ini $PHP_INI_DIR/conf.d/99-app.ini
COPY ./docker/service/xdebug.ini $PHP_INI_DIR/conf.d/docker-php-ext-xdebug.ini

# Install composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

COPY --chown=php . /app

WORKDIR /app

USER php

</code></pre>
<h3>Creating the Makefile</h3>
<p>I find a Makefile a useful addition to any docker-compose setup, as it makes it much easier to access the common commands without needing to remember how a container was named or needed to search to the shell history.</p>
<p>Note: When editing Makefiles make sure to always use tabs instead of spaces especially when indenting commands</p>
<pre><code>.PHONY: run
run:
	@if [ ! -e &quot;.env.local&quot; ]; then\
		cp .env .env.local; \
	fi
	@docker-compose up -d
	@echo &quot;Service is running on http://localhost:9001&quot;

.PHONY: install
install:
	@docker-compose exec --user=&quot;php&quot; -T devbox-service composer install

.PHONY: stop
stop:
	@docker-compose stop

.PHONY: enter
enter:
	@docker-compose exec --user=&quot;php&quot; devbox-service /bin/sh

.PHONY: enter-as-root
enter-as-root:
	@docker-compose exec --user=&quot;root&quot; devbox-service /bin/sh

.PHONY: test
test:
	@docker-compose exec --user=&quot;php&quot; -T devbox-service /bin/sh -c &#x27;APP_ENV=&quot;test&quot; ./bin/phpunit --testdox&#x27;

.PHONY: destroy
destroy:
	@docker-compose down --rmi local

</code></pre>
<h3>Adding Symfony and testing everything</h3>
<p>To test the entire setup we can setup a Symfony application. You can find instructions to do so here: <a href="https://symfony.com/doc/current/setup.html">https://symfony.com/doc/current/setup.html</a> . I went with the LTS version which is 5.4 at the time of the writing.</p>
<p>Note: I also updated the composer file to make sure the database is created. You can skip this if you have another way to make that happen.</p>
<h3>Setting up xDebug</h3>
<p>Debugging can be enabled by uncommenting the contents of the file ./docker/service/xdebug.ini</p>
<p>These are the steps to configure xDebug on PHPStorm:</p>
<ol>
<li>Choose <code>PHP Remote Debugging</code> as CLI interpreter. Make sure local interpreter is removed</li>
<li>Choose <code>Docker Compose</code> as the configuration type and <code>devbox-service</code> as the service</li>
<li>Lifecycle should be <code>Connect to existing container</code></li>
</ol>
<h3>Working demo</h3>
<p>In the docker-compose the port <strong>9001</strong> is mapped for the localhost so you can check everything is working after running:</p>
<pre><code> make run
</code></pre>
<p>to setup the containers</p>
<pre><code> make install
</code></pre>
<p>to install all the composer packages.</p>
<p>There is one endpoint defined called <code>http://localhost:9001/healthz</code> which should return a 200 status code</p>
<p>In order to run the tests, you can run</p>
<pre><code> make test
</code></pre>
<p>and for running other ad-hoc commands like requiring another composer package you can do</p>
<pre><code> make enter
</code></pre>
<h3>Tested using</h3>
<ul>
<li>Ubuntu 21.10</li>
<li>docker version : 20.10.14</li>
<li>docker-compose version : 1.29.1</li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to install .NET Core SDK with ASP.NET on Manjaro Linux]]></title>
            <link>https://alexandrunastase.com/posts/how-to-install-net-core-sdk-with-aspnet-on-manjaro-linux</link>
            <guid>https://alexandrunastase.com/posts/how-to-install-net-core-sdk-with-aspnet-on-manjaro-linux</guid>
            <pubDate>Fri, 25 Sep 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[I recently switched from Fedora to Manjaro as my main Linux distribution and since I use .NET core from time to time I wanted to install it. It turns out that since at the time of this writing, it's not officially supported as it was the case with Fedora, there was a bit more trial and error involved in the process]]></description>
            <content:encoded><![CDATA[<p>I recently switched from Fedora to Manjaro as my main Linux distribution and since I use .NET core from time to time I wanted to install it. It turns out that since at the time of this writing, it&#x27;s not <a href="https://docs.microsoft.com/en-us/dotnet/core/install/linux">officially supported</a>, as it was the case with Fedora, there was a bit more trial and error involved in the process.</p>
<p>My first attempt was to try to install it using <a href="https://snapcraft.io/dotnet-sdk">snap</a> , since it mentions Manjaro in the supported list. I had some issues when I installed it like this, among which the biggest was that the official C# extension in VS Code, OmniSharp, was not working.</p>
<p>The good news is that there are official Arch Linux packages with the .NET Core SDK now.</p>
<h3>Steps to install</h3>
<blockquote>
<p>Install dotnet-sdk with asp.net runtime:</p>
</blockquote>
<pre><code>sudo pacman -S dotnet-sdk aspnet-runtime
</code></pre>
<blockquote>
<p>In order to have the needed environment variables and the dotnet root in PATH, create a<code>/etc/profile.d/dotnet.sh</code> file with the following contents:</p>
</blockquote>
<pre><code>export DOTNET_ROOT=/usr/share/dotnet
export MSBuildSDKsPath=$DOTNET_ROOT/sdk/$(${DOTNET_ROOT}/dotnet --version)/Sdks
export PATH=${PATH}:${DOTNET_ROOT}
</code></pre>
<p>Note: <strong>After this step, a reboot or re-login is required.</strong></p>
<blockquote>
<p>Depending on what shell you use, append to the <code>.bashrc</code> or <code>.zshrc</code> configuration files the following:</p>
</blockquote>
<pre><code># Adds dotnet tools to the PATH variable
export PATH=&quot;$PATH:/home/[[USER_NAME]]/.dotnet/tools&quot;
</code></pre>
<ul>
<li>
<p>This allows tools like <code>dotnet watch</code> or <code>dotnet ef</code> to be available globally</p>
</li>
<li>
<p>Although some people suggest also adding <code>DOTNET_CLI_TELEMETRY_OPTOUT=1</code>, I would advice against it for now, given the fact the Arch Linux is not officially supported and Microsoft might also take into account the usage statistics when prioritizing bug fixes or support for certain Linux distributions.</p>
</li>
</ul>
<h4>Tested using</h4>
<ul>
<li>Manjaro version: 20.1</li>
<li>Kernel version: 5.8.6-1-MANJARO</li>
<li>dotnet version: 3.1.107</li>
</ul>
<h4>Links</h4>
<ul>
<li><a href="https://gist.github.com/waynebloss/55aa06b13a3f647c45b57caa31397089">Manjaro dotnet setup </a></li>
<li><a href="https://wiki.archlinux.org/index.php/.NET_Core">Arch Linux .NET Core</a></li>
<li><a href="https://dev.to/kelvinmai/how-to-install-dotnet-core-in-manjaro-linux-590a">How to install dotnet core in Manjaro Linux</a></li>
</ul>
<p>If this guide becomes outdated or if you have any issues with the any steps feel free PM me on <a href="https://twitter.com/AlexNastase01">Twitter</a> .</p>]]></content:encoded>
        </item>
    </channel>
</rss>