<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: david duymelinck</title>
    <description>The latest articles on DEV Community by david duymelinck (@xwero).</description>
    <link>https://dev.to/xwero</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F235303%2F628f8014-0a74-492d-9d70-5347d8729312.png</url>
      <title>DEV Community: david duymelinck</title>
      <link>https://dev.to/xwero</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/xwero"/>
    <language>en</language>
    <item>
      <title>https://a4al6a.substack.com/p/stop-using-pull-requests thought provoking</title>
      <dc:creator>david duymelinck</dc:creator>
      <pubDate>Fri, 22 May 2026 08:02:53 +0000</pubDate>
      <link>https://dev.to/xwero/httpsa4al6asubstackcompstop-using-pull-requests-thought-provoking-4p78</link>
      <guid>https://dev.to/xwero/httpsa4al6asubstackcompstop-using-pull-requests-thought-provoking-4p78</guid>
      <description>&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://a4al6a.substack.com/p/stop-using-pull-requests" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21hEae%21%2Cf_auto%2Cq_auto%3Abest%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fa4al6a.substack.com%252Ftwitter%252Fsubscribe-card.jpg%253Fv%253D758140240%2526version%253D9" height="480" class="m-0" width="920"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://a4al6a.substack.com/p/stop-using-pull-requests" rel="noopener noreferrer" class="c-link"&gt;
            Stop Using Pull Requests - by Andrea Laforgia
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Your team’s code review process is probably an expensive illusion of quality. Here’s what the evidence says, and what to do instead.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Ficons%2Fsubstack%2Ffavicon.ico" width="32" height="32"&gt;
          a4al6a.substack.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
    </item>
    <item>
      <title>My thoughts on Spatie/Piper</title>
      <dc:creator>david duymelinck</dc:creator>
      <pubDate>Fri, 22 May 2026 07:28:06 +0000</pubDate>
      <link>https://dev.to/xwero/my-thoughts-on-spatiepiper-1ej7</link>
      <guid>https://dev.to/xwero/my-thoughts-on-spatiepiper-1ej7</guid>
      <description>&lt;p&gt;When I saw a &lt;a href="https://github.com/spatie/piper" rel="noopener noreferrer"&gt;library for the pipe operator&lt;/a&gt; I needed to check it out.&lt;/p&gt;

&lt;p&gt;People that read my posts before know I'm a big fan of the pipe operator feature. The main benefit is that it is less likely that the functions have side effects, because they don't depend on a master object that comes with a builder pattern. &lt;/p&gt;

&lt;h2&gt;
  
  
  The good
&lt;/h2&gt;

&lt;p&gt;I like that they moved the collecting function inside the functions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;callable&lt;/span&gt; &lt;span class="nv"&gt;$callback&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Closure&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$callback&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$items&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For people that are less aware how the pipe operator works in PHP 8.5 a short introduction. &lt;br&gt;
The pipe operator passes a single variable on to whatever function is called next in the chain, &lt;code&gt;[1, 2] |&amp;gt; array_sum(...)&lt;/code&gt;. From the moment you want to use predefined arguments for a function, you need to wrap it in a function that collects that single variable, &lt;code&gt;[1, 2] |&amp;gt; (fn($arr) =&amp;gt; array_map(fn (int $i) =&amp;gt; pow($i, 2), $arr))&lt;/code&gt;.&lt;br&gt;
By moving the collecting function into the actual function they reduced the amount of code you need to write to &lt;code&gt;[1, 2] |&amp;gt; map(fn (int $i) =&amp;gt; pow($i, 2))&lt;/code&gt;.&lt;br&gt;
In PHP 8.6 this verbosity problem will be solved, and then you can write &lt;code&gt;[1, 2] |&amp;gt; array_map(fn (int $i) =&amp;gt; pow($i, 2), ?)&lt;/code&gt;. &lt;br&gt;
But because the goal of the library is to mimic the Laravel builder pattern methods, I think it is a great choice.&lt;/p&gt;

&lt;p&gt;I like that all the &lt;a href="https://github.com/spatie/piper/tree/main/src" rel="noopener noreferrer"&gt;functions are in separate files&lt;/a&gt;. The &lt;a href="https://github.com/spatie/piper/blob/main/src/functions.php" rel="noopener noreferrer"&gt;functions.php&lt;/a&gt; file is there just to collect them all.&lt;/p&gt;
&lt;h2&gt;
  
  
  The misses
&lt;/h2&gt;

&lt;p&gt;It is not to hard to see where the name comes from. The problem I have with it is that is too generic. Is it possible they are going to add other pipe operator categories? &lt;/p&gt;

&lt;p&gt;That brings me to the following missed opportunity, why not split the library. There could be a collection-piper and string-piper library. This way the support functions and exceptions context will be clearer. &lt;br&gt;
And you can choose which Laravel builder pattern you want replace, instead of adding functions you are never going to use.&lt;/p&gt;

&lt;p&gt;Because functions are loaded upfront it might be a good idea to have a way to choose the ones the project is really going to use.&lt;br&gt;
It is a problem that has not been addressed by PHP libraries and frameworks, including my own. &lt;br&gt;
By having the functions in separate files this could be more easily achieved than having all the functions in a single file. &lt;/p&gt;

&lt;p&gt;The negative consequence of having the collecting function in the functions, they can't be used for non pipe operator situations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// plain use, pipe operator php 8.6 use&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;callable&lt;/span&gt; &lt;span class="nv"&gt;$callback&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$items&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// pipe operator PHP 8.5/8.6 use&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;pMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;callable&lt;/span&gt; &lt;span class="nv"&gt;$callback&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Closure&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$callback&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;I am a bit of an anti-abstractionist. If you can achieve something with no abstraction, &lt;code&gt;array_map(fn (int $i) =&amp;gt; pow($i, 2), [1, 2])&lt;/code&gt;, do that. As you can see from the example even the pipe operator can be an abstraction.&lt;br&gt;
And that is why Laravel collections and string builders were never my favourite additions to the framework. Even while they have functions that are genuinely helpful. I used &lt;code&gt;map&lt;/code&gt; in the examples, but for me that is not one of the helpful functions.&lt;/p&gt;

&lt;p&gt;By having more pipe operator libraries the use of the functions will be more versatile than with builder pattern methods. But we need to be aware of the limitations if they only target the pipe operator.&lt;/p&gt;

&lt;p&gt;This is quite a short post but I had to get those thoughts out of my head.&lt;/p&gt;

</description>
      <category>php</category>
    </item>
    <item>
      <title>PHP fun: Lean theorem in PHP</title>
      <dc:creator>david duymelinck</dc:creator>
      <pubDate>Wed, 20 May 2026 16:40:47 +0000</pubDate>
      <link>https://dev.to/xwero/php-fun-lean-theorem-in-php-16i8</link>
      <guid>https://dev.to/xwero/php-fun-lean-theorem-in-php-16i8</guid>
      <description>&lt;p&gt;In following post I saw &lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/shrsv/designing-reliable-permission-models-with-lean-4-33lc" class="crayons-story__hidden-navigation-link"&gt;Designing Reliable Permission Models with Lean 4&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/shrsv" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1001514%2F17b7d334-44b1-417a-9268-346e6a34988a.jpg" alt="shrsv profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/shrsv" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Shrijith Venkatramana
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Shrijith Venkatramana
                
              
              &lt;div id="story-author-preview-content-3689093" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/shrsv" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1001514%2F17b7d334-44b1-417a-9268-346e6a34988a.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Shrijith Venkatramana&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/shrsv/designing-reliable-permission-models-with-lean-4-33lc" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;May 17&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/shrsv/designing-reliable-permission-models-with-lean-4-33lc" id="article-link-3689093"&gt;
          Designing Reliable Permission Models with Lean 4
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/productivity"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;productivity&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/programming"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;programming&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/shrsv/designing-reliable-permission-models-with-lean-4-33lc" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;11&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/shrsv/designing-reliable-permission-models-with-lean-4-33lc#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;br&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;increment&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Nat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Nat&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt;
  &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="n"&gt;theorem&lt;/span&gt; &lt;span class="nf"&gt;increment_is_larger&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Nat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;increment&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt;
  &lt;span class="n"&gt;exact&lt;/span&gt; &lt;span class="nc"&gt;Nat&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lt_succ_self&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And I was wondering how close could I get in PHP.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explain Lean like I'm an eighteen year old
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://lean-lang.org/" rel="noopener noreferrer"&gt;Lean&lt;/a&gt; is a language that is a language where tests are a part of the code that gets compiled as you can see in the example. &lt;/p&gt;

&lt;p&gt;With the usual programming languages there is the code, and separate from it there is the mental model that is part in the tests and part in the documentation. &lt;br&gt;
One of the biggest reasons for bugs is that the tests didn't check all options of the mental model, or that the documentation has diverged from the code.&lt;/p&gt;

&lt;p&gt;This has become a bigger problem with the use of AI because of the mountain of code, tests and documentation it can generate.&lt;/p&gt;

&lt;p&gt;This is were Lean tries to bundle the code and the mental model together, which makes it easier for humans and AI to spot errors.&lt;/p&gt;
&lt;h2&gt;
  
  
  Building a similar concept in PHP
&lt;/h2&gt;

&lt;p&gt;Compiling ahead of time is not going to happen in PHP. The closest that it can get is describing the theorem close to the function or method. &lt;br&gt;
This is a job for an attribute.&lt;br&gt;
In Lean all argument values and outputs will be checked at runtime. But in PHP the attribute will be used by a runner.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="na"&gt;#[Attribute(Attribute::TARGET_FUNCTION | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Theorem&lt;/span&gt;
&lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="na"&gt;#[Theorem]&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Theorem&lt;/code&gt; attribute without arguments could be used to run a randomised argument type and/or output type test. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;Nat&lt;/code&gt; in Lean is a natural or positive integer. In PHP I'm going to create an &lt;code&gt;Input&lt;/code&gt; interface with a &lt;code&gt;value&lt;/code&gt; method. The &lt;code&gt;Theorem&lt;/code&gt; attribute is going to have an &lt;code&gt;$inputs&lt;/code&gt; argument which uses the interface. &lt;/p&gt;

&lt;p&gt;For the proof check the PHP 8.5 feature &lt;a href="https://www.php.net/releases/8.5/en.php#closures-in-const-expr" rel="noopener noreferrer"&gt;Closures and First-Class Callables in Constant Expressions&lt;/a&gt; comes in handy.&lt;/p&gt;

&lt;p&gt;Every proof needs a description to distinguish the theorems.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="na"&gt;#[Attribute(Attribute::TARGET_FUNCTION | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Theorem&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$inputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;Closure&lt;/span&gt; &lt;span class="nv"&gt;$proof&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="kt"&gt;Input&lt;/span&gt; &lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="nv"&gt;$inputs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;inputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$inputs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * @return Input[]
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getInputs&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Input&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NaturalNumber&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Input&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Theorem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'is higher'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Input&lt;/span&gt; &lt;span class="nv"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;mixed&lt;/span&gt; &lt;span class="nv"&gt;$output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;NaturalNumber&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I decided for the &lt;code&gt;NaturalNumber&lt;/code&gt; to use a random number. The benefit is that the input mutates, which could surface bugs that where not in frame when adding the theorem. The consequence is that the input needs to be shown to create a repeatable theorem.&lt;/p&gt;

&lt;p&gt;Now for the runner. Because it is a POC I'm going to create a &lt;code&gt;runFunctionTheorems&lt;/code&gt; function that accepts a function as a string.&lt;br&gt;
And it is going to return the function and theorem description as the key and the theorem proof as the value of an array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;runFunctionTheorems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$function&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;is_callable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$function&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InvalidArgumentException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$function&lt;/span&gt;&lt;span class="s2"&gt; is not a function"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nv"&gt;$functionReflection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ReflectionFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$function&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$theoremReflections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$functionReflection&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getAttributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Theorem&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$theoremReflections&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InvalidArgumentException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$function&lt;/span&gt;&lt;span class="s2"&gt; has no Theorem attributes"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nv"&gt;$results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

    &lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$theoremReflections&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$theoremReflection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$theorem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$theoremReflection&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;newInstance&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$inputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;array_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nv"&gt;$test&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getInputs&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="nv"&gt;$actual&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$functionReflection&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="nv"&gt;$inputs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$functionReflection-&amp;gt;name&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$test-&amp;gt;description&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$test&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;proof&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$inputs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nv"&gt;$inputs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$inputs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$actual&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$results&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;runFunctionTheorems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'increment'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;var_export&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// result&lt;/span&gt;
&lt;span class="k"&gt;array&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s1"&gt;'increment is higher'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next iteration will probably be a DTO that changes the runner function output into an array with that type of DTO's. This makes it possible to add the inputs and show the values when the proof has failed.&lt;/p&gt;

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

&lt;p&gt;I have written big test suites and I'm wondering if is going to be feasible to add every possible type of test to the code.&lt;/p&gt;

&lt;p&gt;Having the tests in the code makes it harder to overlook them. My usual workflow is to open the test file and have the code file in a split-screen.&lt;br&gt;
I think having them in one file is a focus improvement.&lt;/p&gt;

&lt;p&gt;The consequence of having the tests in the code is that the runners should not be active in production. And that also means there are attributes that are going to be compiled without any benefit. &lt;br&gt;
There could be a build step that removes the attributes, but then the line numbers don't match with the development code. A solution for that problem is a sourceMap file. &lt;br&gt;
So having the tests separate from the code gives the best cross environment debugging experience. &lt;/p&gt;

&lt;p&gt;Even this simple function test runner has shown me how much work it is to create a test library. And we should thank the people who took that job upon their shoulders every day.&lt;/p&gt;

</description>
      <category>computerscience</category>
      <category>php</category>
      <category>programming</category>
      <category>testing</category>
    </item>
    <item>
      <title>https://en.liujiacai.net/2026/05/16/bun-rust-port/ great read</title>
      <dc:creator>david duymelinck</dc:creator>
      <pubDate>Sat, 16 May 2026 10:50:27 +0000</pubDate>
      <link>https://dev.to/xwero/httpsenliujiacainet20260516bun-rust-port-great-read-2dol</link>
      <guid>https://dev.to/xwero/httpsenliujiacainet20260516bun-rust-port-great-read-2dol</guid>
      <description>&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://en.liujiacai.net/2026/05/16/bun-rust-port/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffs.liujiacai.net%2Fcdn-img%2Fzigcc%2Fbun-rust-port.webp" height="436" class="m-0" width="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://en.liujiacai.net/2026/05/16/bun-rust-port/" rel="noopener noreferrer" class="c-link"&gt;
            My Thoughts on Bun's Rust Rewrite | Jiacai Liu's personal website
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Before we discuss Rewrite Bun in Rust, there's something that needs to be said, because no one is saying it.
Bun stands where it does today because of Zig.
Jarred chose Zig back then not because it was "cool," but because Zig enabled a small team to rapidly prototype a high-performance JS runtime without a GC, without a heavy runtime. Zig's low friction, direct memory manipulation, and straightforward C interop were the core reasons Bun could punch above its weight on performance with an extremely small team in its early days. The architecture, data structures, and low-level design of Bun that you see today – that was shaped by Zig.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fliujiacai.net%2Ffavicon.ico" width="48" height="48"&gt;
          en.liujiacai.net
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>javascript</category>
      <category>performance</category>
      <category>rust</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Composer is just a console application</title>
      <dc:creator>david duymelinck</dc:creator>
      <pubDate>Sat, 16 May 2026 10:04:45 +0000</pubDate>
      <link>https://dev.to/xwero/composer-is-just-a-console-application-30jo</link>
      <guid>https://dev.to/xwero/composer-is-just-a-console-application-30jo</guid>
      <description>&lt;p&gt;I want to preface the post with two remarks.&lt;/p&gt;

&lt;p&gt;Yes adding just to the title is clickbait. It is like saying Symfony's &lt;code&gt;bin/console&lt;/code&gt; and Laravel's &lt;code&gt;artisan&lt;/code&gt; are just console applications.&lt;/p&gt;

&lt;p&gt;I'm embarrassed I never took the time to understand &lt;a href="https://getcomposer.org" rel="noopener noreferrer"&gt;Composer&lt;/a&gt; until now. I have been preaching for a long time to start each PHP project with Composer, even when the project is not going end up on &lt;a href="https://packagist.org/" rel="noopener noreferrer"&gt;Packagist&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How did I get here
&lt;/h2&gt;

&lt;p&gt;I recently did an &lt;a href="https://dev.to/xwero/my-2-cents-on-yii3-28i0"&gt;exploration of Yii3&lt;/a&gt;. And in the &lt;a href="https://github.com/yiisoft/app/blob/master/config/configuration.php" rel="noopener noreferrer"&gt;configuration file&lt;/a&gt; I saw something that caught my eye.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// NOTE: After making changes in this file, run `composer yii-config-rebuild` to update the merge plan.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So I dug deeper and found out the command is part of the &lt;a href="https://github.com/yiisoft/config" rel="noopener noreferrer"&gt;Yii3 config component&lt;/a&gt;. I will explain later how they did it, but if you are curious try to find out for yourself. This post will still be here when you get back.&lt;/p&gt;

&lt;p&gt;The second trigger were &lt;a class="mentioned-user" href="https://dev.to/suckup_de"&gt;@suckup_de&lt;/a&gt; &lt;a href="https://github.com/voku/itp-context/tree/main/bin" rel="noopener noreferrer"&gt;itp-context library's cli commands&lt;/a&gt;. &lt;br&gt;
Having to use &lt;code&gt;vendor/bin/itp-context-summarize&lt;/code&gt; and &lt;code&gt;vendor/bin/itp-context-validate&lt;/code&gt; triggered my developer laziness. At first my thoughts went to creating a console application like &lt;code&gt;console/bin&lt;/code&gt;. But then you still need it to put it in the vendor bin and the command would be &lt;code&gt;vendor/bin/itp-context summarize&lt;/code&gt;. This only helps with the discoverability of the commands not with typing less.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Composer plugin way
&lt;/h2&gt;

&lt;p&gt;Composer allows you to &lt;a href="https://getcomposer.org/doc/articles/plugins.md#command-provider" rel="noopener noreferrer"&gt;add commands using plugins&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;I'm going to show how it is done using the &lt;code&gt;vendor/bin/itp-context-summarize&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;The first step might be a hurdle for some people but the commands should be a project on their own. The main reason is that for a composer plugin to work the type in composer.json has to be &lt;code&gt;composer-plugin&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"voku/itp-context-commands"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"composer-plugin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"require"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"composer-plugin-api"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^2.0"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"require-dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"composer/composer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^2.0"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"autoload"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"psr-4"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"ItpContextCommands\\"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/"&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"extra"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"class"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ItpContextCommands&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;Plugin"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The plugin code is very simple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Plugin&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;PluginInterface&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Capable&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;activate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Composer&lt;/span&gt; &lt;span class="nv"&gt;$composer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;IOInterface&lt;/span&gt; &lt;span class="nv"&gt;$io&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// void&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getCapabilities&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;CommandProvider&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;ItpContextCommandProvider&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * @inheritDoc
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;deactivate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Composer&lt;/span&gt; &lt;span class="nv"&gt;$composer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;IOInterface&lt;/span&gt; &lt;span class="nv"&gt;$io&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// void&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * @inheritDoc
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;uninstall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Composer&lt;/span&gt; &lt;span class="nv"&gt;$composer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;IOInterface&lt;/span&gt; &lt;span class="nv"&gt;$io&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// void&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;ItpContextCommandProvider&lt;/code&gt;  is even more simple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ItpContextCommandProvider&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;CommandProvider&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * @inheritDoc
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getCommands&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SummarizeCommand&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that the bootstrapping is done, the work on the actual commands can begin.&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="na"&gt;#[AsCommand('itp-context:summarize')]&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SummarizeCommand&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseCommand&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;InputInterface&lt;/span&gt; &lt;span class="nv"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;OutputInterface&lt;/span&gt; &lt;span class="nv"&gt;$output&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// the real code&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Command&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SUCCESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might have noticed the &lt;code&gt;BaseCommand&lt;/code&gt; as parent instead of &lt;code&gt;Command&lt;/code&gt; or no parent at all. This is a Composer restriction.&lt;/p&gt;

&lt;p&gt;Now the library can add &lt;code&gt;voku/itp-context-commands&lt;/code&gt; to the composer require section. And when the library is installed Composer will ask the question that the plugin may be used. When you allowed it, you can use &lt;code&gt;composer itp-context:summarize&lt;/code&gt; or &lt;code&gt;composer itp:summarize&lt;/code&gt;. And that makes my developer lazy part happy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thinking bigger
&lt;/h2&gt;

&lt;p&gt;Most PHP solutions have a console application, &lt;code&gt;bin/console&lt;/code&gt; and &lt;code&gt;artisan&lt;/code&gt; are the most known. Yii has &lt;code&gt;yii&lt;/code&gt;. Statamic has &lt;a href="https://github.com/statamic/statamic/blob/6.x/please" rel="noopener noreferrer"&gt;please&lt;/a&gt; next to &lt;code&gt;artisan&lt;/code&gt;.&lt;br&gt;
And that got me thinking, what if you could merge those applications with composer to have a single point of entry for all commands. &lt;/p&gt;

&lt;p&gt;For this to work the Composer plugin API could extend the &lt;a href="https://github.com/composer/composer/blob/main/src/Composer/Plugin/Capability/CommandProvider.php" rel="noopener noreferrer"&gt;CommandProvider&lt;/a&gt; interface with a &lt;code&gt;getApplication&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;For Symfony the &lt;code&gt;CommandProvider&lt;/code&gt; child could be something like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Kernel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Bundle\FrameworkBundle\Console\Application&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nc"&gt;SymfonyApplication&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\Console\Application&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SymfonyCommandProvider&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;CommandProvider&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getCommands&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getApplication&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;Application&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nv"&gt;$kernel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Kernel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'APP_ENV'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'APP_DEBUG'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SymfonyApplication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$kernel&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Composer &lt;code&gt;Application&lt;/code&gt; could then have a &lt;code&gt;getPluginApplicationCommands&lt;/code&gt; method. A naive implementation could be.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getPluginApplicationCommands&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nv"&gt;$commands&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

   &lt;span class="nv"&gt;$composer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getComposer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nv"&gt;$composer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$composer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Factory&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;createGlobal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;disablePluginsByDefault&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;disableScriptsByDefault&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;

     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nv"&gt;$composer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$pm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$composer&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getPluginManager&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pm&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getPluginCapabilities&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Composer\Plugin\Capability\CommandProvider'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'composer'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$composer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'io'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$capability&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="nv"&gt;$application&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$capability&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getApplication&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
           &lt;span class="c1"&gt;// ConsoleApplication is alias of Symfony\Component\Console\Application&lt;/span&gt;
           &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$application&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;ConsoleApplication&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="nv"&gt;$commands&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$application&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
           &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$commands&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;          
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that method can then be used to add the commands. &lt;/p&gt;

&lt;p&gt;The biggest problem with the change at this moment is that the commands from other applications can overwrite the Composer commands.&lt;br&gt;
To prevent this the &lt;code&gt;CommandProvider&lt;/code&gt; interface could be extended with the &lt;code&gt;getApplicationNamespace&lt;/code&gt; method.&lt;br&gt;
This requires the following changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Kernel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Bundle\FrameworkBundle\Console\Application&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nc"&gt;SymfonyApplication&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\Console\Application&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SymfonyCommandProvider&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;CommandProvider&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getCommands&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getApplication&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;Application&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nv"&gt;$kernel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Kernel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'APP_ENV'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'APP_DEBUG'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SymfonyApplication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$kernel&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getApplicationNamespace&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'symfony'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getPluginApplicationCommands&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nv"&gt;$commands&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

   &lt;span class="nv"&gt;$composer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getComposer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nv"&gt;$composer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$composer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Factory&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;createGlobal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;disablePluginsByDefault&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;disableScriptsByDefault&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;

     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nv"&gt;$composer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$pm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$composer&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getPluginManager&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pm&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getPluginCapabilities&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Composer\Plugin\Capability\CommandProvider'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'composer'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$composer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'io'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$capability&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="nv"&gt;$application&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$capability&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getApplication&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
           &lt;span class="c1"&gt;// ConsoleApplication is alias of Symfony\Component\Console\Application&lt;/span&gt;
           &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$application&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;ConsoleApplication&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="nv"&gt;$commands&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$application&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
           &lt;span class="p"&gt;}&lt;/span&gt;

           &lt;span class="nv"&gt;$namespace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$capability&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getApplicationNamespace&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

           &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nv"&gt;$namespace&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$commands&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;$command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                 &lt;span class="nv"&gt;$command&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$namespace&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;':'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$command&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
              &lt;span class="p"&gt;}&lt;/span&gt;
           &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$commands&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;          
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;I'm wondering if there is consequence that stops us from using Composer as a centralised command entry point? &lt;br&gt;
Did Symfony set the example by using a separate file for the application, and did the rest follow the example?&lt;br&gt;
Why did Symfony choose to add the bin directory to the root directory of the project instead of using the &lt;a href="https://getcomposer.org/doc/04-schema.md#bin" rel="noopener noreferrer"&gt;bin&lt;/a&gt; key in composer.json?&lt;/p&gt;

&lt;p&gt;I got a lot more questions than answers after this Composer plugin deep dive.&lt;br&gt;
The good thing is that I discovered more options with a solution I use daily.&lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>symfony</category>
    </item>
    <item>
      <title>My 2 cents on Yii3</title>
      <dc:creator>david duymelinck</dc:creator>
      <pubDate>Thu, 14 May 2026 09:49:25 +0000</pubDate>
      <link>https://dev.to/xwero/my-2-cents-on-yii3-28i0</link>
      <guid>https://dev.to/xwero/my-2-cents-on-yii3-28i0</guid>
      <description>&lt;p&gt;I got to exploring &lt;a href="https://yii3.yiiframework.com/" rel="noopener noreferrer"&gt;Yii3&lt;/a&gt; via &lt;a href="https://craftcms.com/" rel="noopener noreferrer"&gt;Craft CMS&lt;/a&gt;. Craft is moving to &lt;a href="https://craftcms.com/blog/craft-6-alpha-released" rel="noopener noreferrer"&gt;Laravel with their sixth version&lt;/a&gt;. When I saw the announcement I was thinking Yii was having problems because Yii3 was stuck for a long time on 80 precent, if I can remember that right.&lt;br&gt;
But now I have seen Yii3 I think the decision is more because they needed change their guts anyway when they update to Yii3.&lt;/p&gt;

&lt;h2&gt;
  
  
  Major changes
&lt;/h2&gt;

&lt;p&gt;Yii2 is, like most other frameworks, &lt;a href="https://github.com/yiisoft/yii2" rel="noopener noreferrer"&gt;one pre-configured bundle&lt;/a&gt;. With Yii3 all components are separate and the glue that binds them together are the templates; &lt;a href="https://github.com/yiisoft/app" rel="noopener noreferrer"&gt;web&lt;/a&gt;, &lt;a href="https://github.com/yiisoft/app-api" rel="noopener noreferrer"&gt;api&lt;/a&gt;, &lt;a href="https://github.com/yiisoft/app-console" rel="noopener noreferrer"&gt;console&lt;/a&gt;. The workhorse of the templates is the configuration.&lt;/p&gt;

&lt;p&gt;This loose structure allows the framework to use different setups depending on the need of the application. &lt;br&gt;
There are &lt;a href="https://github.com/yiisoft?q=runner&amp;amp;type=all&amp;amp;language=&amp;amp;sort=" rel="noopener noreferrer"&gt;multiple runners&lt;/a&gt; to pick your flavour of PHP runtime.&lt;br&gt;
You can use the &lt;a href="https://yiisoft.github.io/docs/guide/start/databases.html" rel="noopener noreferrer"&gt;database package&lt;/a&gt; you prefer.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://yiisoft.github.io/docs/guide/intro/upgrade-from-v2.html#use-di-instead-of-the-service-locator" rel="noopener noreferrer"&gt;use of &lt;code&gt;Yii::$app-&amp;gt;&lt;/code&gt; is discouraged&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Routes can now be &lt;a href="https://yiisoft.github.io/docs/guide/structure/action.html" rel="noopener noreferrer"&gt;any callable&lt;/a&gt; you want, which brings Yii3 to the same level as the other frameworks. &lt;/p&gt;

&lt;p&gt;No &lt;a href="https://github.com/yiisoft/yii2/blob/master/composer.json#L78" rel="noopener noreferrer"&gt;frontend related packages&lt;/a&gt; as Composer dependencies. This was the biggest problem for me to use the previous version. A backend framework should be frontend agnostic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where they can improve
&lt;/h2&gt;

&lt;p&gt;The configuration is very confusing at first sight. &lt;br&gt;
The &lt;a href="https://github.com/yiisoft/app/blob/master/config/configuration.php" rel="noopener noreferrer"&gt;main configuration file&lt;/a&gt; has variables in it as strings.&lt;br&gt;
The &lt;a href="https://github.com/yiisoft/app/blob/master/config/common/di/application.php" rel="noopener noreferrer"&gt;dependency injection configuration&lt;/a&gt; looks more like a Wordpress configuration than a modern framework configuration. &lt;br&gt;
While I get it that using arrays is the most versatile way to allow all the configuration options, most frameworks have started to use attributes for a better developer experience. &lt;/p&gt;

&lt;p&gt;It is still using &lt;a href="https://github.com/nikic/FastRoute" rel="noopener noreferrer"&gt;nikic/FastRoute&lt;/a&gt;. While it was a good router library, others implemented the same technique and got faster after further development.&lt;/p&gt;

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

&lt;p&gt;I do think with Yii3 they moved the framework in the right direction. The closest framework in spirit to Yii3 that comes to mind is &lt;a href="https://docs.laminas.dev/" rel="noopener noreferrer"&gt;Laminas&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The question is will it be enough to survive when the most known solution build on it is moving away? &lt;br&gt;
The numbers on &lt;a href="https://yii3.yiiframework.com/" rel="noopener noreferrer"&gt;their website&lt;/a&gt; are mainly from the use of the previous versions of the framework. A big change will always scare people away. &lt;/p&gt;

</description>
      <category>php</category>
      <category>yii</category>
    </item>
    <item>
      <title>Symfony 8.1, the big CLI update</title>
      <dc:creator>david duymelinck</dc:creator>
      <pubDate>Fri, 08 May 2026 16:08:29 +0000</pubDate>
      <link>https://dev.to/xwero/symfony-81-the-big-cli-update-ifk</link>
      <guid>https://dev.to/xwero/symfony-81-the-big-cli-update-ifk</guid>
      <description>&lt;p&gt;When I saw the change list in the &lt;a href="https://symfony.com/blog/symfony-8-1-0-beta1-released" rel="noopener noreferrer"&gt;Symfony 8.1 beta 1 announcement&lt;/a&gt; The first thing that I noticed is that there are a lot of &lt;a href="https://symfony.com/doc/current/console.html" rel="noopener noreferrer"&gt;Console&lt;/a&gt; and CLI related changes.&lt;br&gt;
The &lt;a href="https://symfony.com/blog/category/living-on-the-edge/8.1" rel="noopener noreferrer"&gt;first few "New in Symfony 8.1" posts&lt;/a&gt; confirm this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Added the TUI component
&lt;/h2&gt;

&lt;p&gt;I have written about the &lt;a href="https://dev.to/xwero/exploring-the-symfony-tui-component-5674"&gt;TUI component&lt;/a&gt; but now it is &lt;a href="https://github.com/symfony/symfony/pull/63778" rel="noopener noreferrer"&gt;a part of the framework&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The quick explanation for the people who are unaware of the component. &lt;br&gt;
Think Claude code/OpenCode, Midnight commander/Yazi, basically everything in &lt;a href="https://github.com/rothgar/awesome-tuis" rel="noopener noreferrer"&gt;Awesome TUI's&lt;/a&gt; you can now make in PHP.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTTP-less application
&lt;/h2&gt;

&lt;p&gt;While &lt;a href="https://symfony.com/blog/new-in-symfony-8-1-http-less-symfony-applications" rel="noopener noreferrer"&gt;this change&lt;/a&gt; goes further than only Console, it streamlines creating commands with multiple dependencies or config values. &lt;/p&gt;

&lt;p&gt;Up till now when you didn't want the HTTP functionality of the Kernel, you needed to inject the services explicitly or create your own container.&lt;br&gt;
While they are still options, adding a HTTP-less Kernel causes less friction because of the familiarity with the web kernel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Input improvements
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://symfony.com/blog/new-in-symfony-8-1-console-argument-resolvers" rel="noopener noreferrer"&gt;Argument resolvers in Console&lt;/a&gt; is a big one. &lt;br&gt;
This brings command arguments on the same level as the Controller arguments. &lt;br&gt;
&lt;a href="https://symfony.com/blog/new-in-symfony-8-1-method-based-commands" rel="noopener noreferrer"&gt;Method based commands&lt;/a&gt; makes it possible to group commands like you would with a Controller. While it gives you more options the argument resolvers addition allows to inject services as an method argument so there is less need to use constructor dependency injection. &lt;/p&gt;

&lt;p&gt;I'm sure a future article in the new series will be the &lt;a href="https://github.com/symfony/symfony/pull/63293" rel="noopener noreferrer"&gt;image support for &lt;code&gt;QuestionHelper&lt;/code&gt; and &lt;code&gt;#Ask&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
This will only work on terminals that support image rendering. But having the distinction between images and other files gives you more options.&lt;/p&gt;

&lt;p&gt;It will be possible to add validation constraints to &lt;a href="https://github.com/symfony/symfony/pull/63359" rel="noopener noreferrer"&gt;&lt;code&gt;QuestionHelper&lt;/code&gt; and &lt;code&gt;#Ask&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/symfony/symfony/pull/63714" rel="noopener noreferrer"&gt;&lt;code&gt;#MapInput&lt;/code&gt;&lt;/a&gt;. &lt;br&gt;
It is already possible to add a Validator, but the using the constraints makes the argument a lot cleaner.&lt;/p&gt;

&lt;p&gt;Adding &lt;a href="https://github.com/symfony/symfony/pull/63183" rel="noopener noreferrer"&gt;any type as a default for an argument&lt;/a&gt; is going bring more type safety to command arguments. The current restriction felt arbitrary.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/symfony/symfony/pull/57598" rel="noopener noreferrer"&gt;Exposing the raw input values&lt;/a&gt; is great when the command exposes another command you want to limit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/symfony/symfony/pull/52055" rel="noopener noreferrer"&gt;Preventing incompatible mode flags&lt;/a&gt; might be a less important change after this list, but I think it is an important one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Miscellaneous
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/symfony/symfony/pull/62112/changes" rel="noopener noreferrer"&gt;Added OSC progress state&lt;/a&gt;. You probably seen the progressbar on top of the page when you are reading an article on a website. This is also possible in the terminal. I didn't know that, but it is nice when you scrolled up in the terminal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/symfony/symfony/pull/63538" rel="noopener noreferrer"&gt;The partial command name list is now alphabetical&lt;/a&gt;. Having order is always better than chaos.&lt;/p&gt;

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

&lt;p&gt;Of course the release contains other features, like &lt;a href="https://symfony.com/blog/new-in-symfony-8-1-deep-cloner" rel="noopener noreferrer"&gt;DeepCloner&lt;/a&gt;, but the main focus seems to be on CLI features. &lt;/p&gt;

&lt;p&gt;And with the HTTP-less application the framework is moving from a web focused framework to a general use framework. Where the TUI component showed PHP CLI code isn't only for basic commands, Symfony 8.1 shows it can do more than only websites. It was already possible when you brought your own tools, but now you can do it using Symfony conventions. &lt;/p&gt;

&lt;p&gt;This is going to be a great release!&lt;/p&gt;

</description>
      <category>php</category>
      <category>symfony</category>
    </item>
    <item>
      <title>Where to store slugs</title>
      <dc:creator>david duymelinck</dc:creator>
      <pubDate>Thu, 30 Apr 2026 18:38:14 +0000</pubDate>
      <link>https://dev.to/xwero/where-to-store-slugs-26lp</link>
      <guid>https://dev.to/xwero/where-to-store-slugs-26lp</guid>
      <description>&lt;p&gt;A few days ago I saw a post about a slug library. And they used specific ORM models, post and author, to store the slugs.&lt;br&gt;
A slug is a part of the routing. And while they can be stored in a database, the best location is not as a field of a content table.&lt;/p&gt;

&lt;h2&gt;
  
  
  The basics
&lt;/h2&gt;

&lt;p&gt;As I mentioned a slug is a part of the routing. In the case of a post that can be where-to-store-slugs. This can be the full path, /where-to-store-slugs, but it also can be a part of the path, /posts/where-to-store-slugs. &lt;br&gt;
When your website is multilingual there can be aliases for the slug.&lt;br&gt;
For the people that go old school it can a part of the query string, ?title=where-to-store-slugs.&lt;/p&gt;

&lt;p&gt;On a mostly static website you can get away with putting the slugs in the routing configuration.&lt;br&gt;
When the urls are more dynamic, I'm thinking about user created pages, the routing configuration can still be an option when you include an environment specific configuration. The problem there is that the routing configuration is cached most of the time to make the routing as fast as possible. So every slug change could create a refresh cache event.&lt;/p&gt;

&lt;p&gt;The second fastest thing of your website is the database. So that is why the library, and others like it, use that as the storage solution. &lt;/p&gt;

&lt;h2&gt;
  
  
  What is wrong with storing slugs as a part of an ORM model?
&lt;/h2&gt;

&lt;p&gt;Because a slug can be the full path, most libraries have an option to check the uniqueness of slug. You don't want duplicate urls. &lt;br&gt;
The problem is that most libraries only check the uniqueness for the specific model, not for every model that has slugs.&lt;br&gt;
For example an author can have the slug mike and a post can have a slug mike, because the post title is an acronym. Most libraries lower case the slugs they generate. &lt;/p&gt;

&lt;p&gt;Having the slug as a part of the path is a fix, so now you have post/mike and author/mike. Now they are unique in their respective path, but you needed to change the routing to fix a uniqueness problem in the database. This is too tight of a coupling to not cause problems in the future. &lt;/p&gt;

&lt;p&gt;A more extreme example is /mike/mike. It could be the authors page as the parent to give context to the post. But it could also be the post that is the parent to give context to the author.&lt;br&gt;
I have seen editors have all sorts of weird rules to make urls unique, but there were always occasions where they managed to create duplicate urls.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the better solution?
&lt;/h2&gt;

&lt;p&gt;As I mentioned before an environment specific configuration for dynamic urls is an option.&lt;/p&gt;

&lt;p&gt;When you want to store the slugs in the database use a separate table. That table contains the urls with the slugs, their matching controllers and extra metadata if needed. &lt;br&gt;
Another solution is to create an url tree, this is a good solution when you expect many urls with multiple levels. &lt;/p&gt;

&lt;p&gt;The first two solutions make it easier to check the uniqueness because they centralise the slugs.&lt;br&gt;
The url tree is like the prefix example, it only needs to check the uniqueness in a specific branch.&lt;/p&gt;

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

&lt;p&gt;While a library that adds a slug to an ORM model might seem like a quick solution. I think most of the times they are a solution with a short lifespan.&lt;/p&gt;

&lt;p&gt;The best way of thinking about slugs is to see where they fit in the website routing strategy. &lt;br&gt;
When slugs are always a part of paths with prefixes or suffixes, the library could be the solution the website needs. &lt;/p&gt;

&lt;p&gt;To be clear this is not a rant against one library in particular, I seen multiple libraries in different languages that have the same pattern. &lt;br&gt;
My message for the library builders is to move the storage to the edge of the library, don't make it the main feature.&lt;/p&gt;

&lt;p&gt;Using a slug might seems an insignificant detail, but there is always more than meets the eye.&lt;/p&gt;

</description>
      <category>architecture</category>
    </item>
    <item>
      <title>Fixing the array functions for the pipe operator</title>
      <dc:creator>david duymelinck</dc:creator>
      <pubDate>Tue, 28 Apr 2026 21:57:34 +0000</pubDate>
      <link>https://dev.to/xwero/fixing-the-array-functions-for-the-pipe-operator-1j6e</link>
      <guid>https://dev.to/xwero/fixing-the-array-functions-for-the-pipe-operator-1j6e</guid>
      <description>&lt;p&gt;Update: I released it as a &lt;a href="https://packagist.org/packages/xwero/paf" rel="noopener noreferrer"&gt;package&lt;/a&gt;. When you try it give some feedback.&lt;/p&gt;

&lt;p&gt;I had a good conversation with &lt;a class="mentioned-user" href="https://dev.to/delacry"&gt;@delacry&lt;/a&gt; over &lt;a href="https://dev.to/delacry/type-safe-collections-in-php-84-what-i-wish-arrays-had-4gof"&gt;the library that was introduced&lt;/a&gt; .&lt;/p&gt;

&lt;p&gt;And what struck me the most was the last example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$topAdmins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$users&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;array_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$u&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isActive&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;array_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$u&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;array_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$u&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isAdmin&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$us&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;usort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$us&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$b&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nv"&gt;$us&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$us&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;array_slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is an example with the upcoming partial function application change (PHP 8.6). &lt;br&gt;
I do agree, while it looks miles better than it is in PHP 8.5, it still is not that straight forward as the solutions with the builder pattern.&lt;/p&gt;

&lt;p&gt;So I took a bit of time off from my dates project to come up with &lt;a href="https://codeberg.org/xwero/PAF" rel="noopener noreferrer"&gt;PAF&lt;/a&gt;. POAF looked weird to me, and PAF reminded me of a thing we said in Dutch when I was young, pief poef paf. So that is the whole story behind the name.&lt;/p&gt;

&lt;p&gt;The example with the library looks like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$topadmins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$users&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;filterValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$u&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isActive&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;mapSingle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$u&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;filterValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$u&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isAdmin&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;sortValuesCustom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$b&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;array_slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course I'm biased, but I don't think anyone is going to say it looks worse.&lt;br&gt;
Functions where the array is the first argument and return an array, like &lt;code&gt;array_slice&lt;/code&gt;, don't need to be wrapped.&lt;/p&gt;

&lt;p&gt;It is a bit more than a dumb wrapper for the array functions.&lt;br&gt;
Another good point from the conversation was that &lt;code&gt;array_filter&lt;/code&gt; leaves gaps in the keys. You could use &lt;code&gt;array_values&lt;/code&gt;, but that only solves the problem for the integer keys. For string keys the solution is more complicated.&lt;/p&gt;

&lt;p&gt;So I created a &lt;code&gt;reIndex&lt;/code&gt; function that takes care of three cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;integers&lt;/li&gt;
&lt;li&gt;strings &lt;/li&gt;
&lt;li&gt;custom logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the example you see that is triggered by adding &lt;code&gt;true&lt;/code&gt; to the second use of the &lt;code&gt;filterValues&lt;/code&gt; function in the chain. &lt;br&gt;
I know using a boolean is controversial, but with named arguments you can do &lt;code&gt;reIndex: true&lt;/code&gt;. I'm using them more and more, because they are code as documentation.&lt;/p&gt;

&lt;p&gt;I went a bit overboard and I added the function to the map functions. The only use case I can think of at the moment, is using the custom logic to manipulate the keys before going to the next function in the chain.&lt;/p&gt;

&lt;p&gt;It is still early days, and I already have a few other directions I want to take for the library. For example the &lt;code&gt;sortValues&lt;/code&gt; and &lt;code&gt;sortValuesCustom&lt;/code&gt; functions could be one.&lt;/p&gt;

&lt;p&gt;It is a small library but I think it has enough value to make it a package. But it has a few more iterations to go before it is ready to be released.&lt;/p&gt;

</description>
      <category>php</category>
    </item>
    <item>
      <title>AI agent attention shattering</title>
      <dc:creator>david duymelinck</dc:creator>
      <pubDate>Sun, 26 Apr 2026 05:44:39 +0000</pubDate>
      <link>https://dev.to/xwero/ai-agent-attention-shattering-35jn</link>
      <guid>https://dev.to/xwero/ai-agent-attention-shattering-35jn</guid>
      <description>&lt;p&gt;For the library I'm working on I finally getting the power of running multiple agents in parallel. The problem I discovered is that the ping that the agent sends when the job is done is affecting my attention span, like chat app or email pings.&lt;/p&gt;

&lt;p&gt;A while ago Boris Cherny, who created Claude Code, mentioned on X that he had 8 tabs with agents open. &lt;br&gt;
The question I have is how many of those tabs are forgotten for days or even longer?&lt;br&gt;
I found out 4 agents running in parallel is my maximum, while maintaining a workflow that is not doing work between job-done-pings.&lt;br&gt;
More than 4 agents gave me more pressure to check when they are done. Maybe over time I will develop a higher tolerance, but for now that is my tipping point.&lt;/p&gt;

&lt;p&gt;I can understand that people that are more into AI will start longer running tasks. But I'm running short tasks, because those get the best results.&lt;/p&gt;

&lt;p&gt;Before AI came onto the scene, many developers had the idea two monitors were better than one. But after a while a lot discovered the bigger screen size is just a way to add more distractions.&lt;br&gt;
Two screens are good when text needs to be instantly checked against a visual representation. But for most developers that instant checking ability is not needed. We have tests that do the checking for us.&lt;/p&gt;

&lt;p&gt;When we were forced to stay at home. One of the positive conclusions was that for mentally intensive work we needed a time were nothing distracted us. And now people at the office also set blocks of do-not-disturb-time in their calendar when they need that distraction free time. &lt;/p&gt;

&lt;p&gt;And here we are introducing a new attention seeker with multiple AI agents.&lt;br&gt;
While running AI agents in parallel will make you more productive. This also can make you lose oversight when you don't control the workflow. &lt;/p&gt;

&lt;p&gt;How do you handle multiple agents at once in your workflow?&lt;br&gt;
Do you feel like a person that can multitask? Should we multitask? &lt;/p&gt;

</description>
      <category>ai</category>
      <category>discuss</category>
    </item>
    <item>
      <title>My 2 cents on the Spatie guidelines agent skills</title>
      <dc:creator>david duymelinck</dc:creator>
      <pubDate>Sat, 18 Apr 2026 06:41:37 +0000</pubDate>
      <link>https://dev.to/xwero/my-2-cents-on-spatie-guidelines-agent-skill-1jl4</link>
      <guid>https://dev.to/xwero/my-2-cents-on-spatie-guidelines-agent-skill-1jl4</guid>
      <description>&lt;p&gt;I saw on &lt;a href="https://laravel-news.com/spatie-shares-their-coding-guidelines-as-ai-skills" rel="noopener noreferrer"&gt;Laravel-news&lt;/a&gt; a post about &lt;a href="https://github.com/spatie/guidelines-skills" rel="noopener noreferrer"&gt;Spatie guidelines&lt;/a&gt;. Because other peoples guidelines are always a chance to learn something new, I thought it would be fun to read through  them and provide comments.&lt;br&gt;
I'm going to focus on the PHP/Laravel guidelines.&lt;/p&gt;




&lt;p&gt;The PHP agent skill is split up in two files where most of the information in &lt;a href="https://github.com/spatie/guidelines-skills/blob/main/resources/boost/skills/spatie-laravel-php/SKILL.md" rel="noopener noreferrer"&gt;SKILL.md&lt;/a&gt; is repeated in &lt;a href="https://github.com/spatie/guidelines-skills/blob/main/resources/boost/skills/spatie-laravel-php/references/spatie-laravel-php-guidelines.md" rel="noopener noreferrer"&gt;spatie-laravel-php-guidelines.md&lt;/a&gt;. This seems a waste of tokens.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;Use camelCase for non-public-facing strings&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I assume the model(s) don't always follow that rule of &lt;a href="https://www.php-fig.org/psr/psr-12/" rel="noopener noreferrer"&gt;PSR-12&lt;/a&gt;. It is not explicitly mentioned, so it can only be inferred by reading the examples.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;Use short nullable notation: ?string not string|null&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is explicitly mentioned in PSR-12.&lt;br&gt;&lt;br&gt;
I understand the reasoning behind the rule because of the other PHP functionality using the question mark to signal &lt;code&gt;null&lt;/code&gt; like &lt;code&gt;$client?-&amp;gt;address&lt;/code&gt; and &lt;code&gt;$client ?? new Client()&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
My own take is to use the long notation to be consistent with the cases where &lt;code&gt;null&lt;/code&gt; is a part of three or more options, &lt;code&gt;string|int|null&lt;/code&gt;. It is one less place where your mind needs to make the switch that a question mark means null.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;Use typed properties, not docblocks&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'm all for documenting using code. It is one of the reasons I'm against using array shapes in docblocks. When the shape is that important use a type.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;Use constructor property promotion when all properties can be promoted&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With the existence of &lt;a href="https://www.php.net/manual/en/functions.arguments.php#functions.variable-arg-list" rel="noopener noreferrer"&gt;a variable arguments list&lt;/a&gt; this is a bit of a tricky guideline. &lt;br&gt;
A variable arguments list is a more solid way to pass input than an array.&lt;br&gt;
So when a model follows that rule, all none variable properties aren't going to get promoted?&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;Document iterables with generics:&lt;br&gt;
&lt;code&gt;/** @return Collection&amp;lt;int, User&amp;gt; */&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A better way would be to create a specific collection type for the Eloquent model output. It used to be more (too much?) effort but with code generation it is not going to matter.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;Always import classnames in docblocks; never use fully qualified names:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is good advice! &lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;Happy path last: handle error conditions first, success case last&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While it is a good rule, it is not always possible.&lt;/p&gt;



&lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isActive&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In the same code block with ternary examples this looks very drawn out.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;?-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isActive&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I understand it is an example of multiple if's over multiple branches. But reducing the if's in a function is also a good goal.&lt;/p&gt;



&lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Ternary instead of else&lt;/span&gt;
&lt;span class="nv"&gt;$condition&lt;/span&gt;
    &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;doSomethingElse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For me this looks like using &lt;code&gt;match&lt;/code&gt; when it is better to use &lt;code&gt;switch&lt;/code&gt;. &lt;br&gt;
While the ternary operator is not explicitly bound  to a return. The &lt;a href="https://www.php.net/manual/en/language.operators.comparison.php#language.operators.comparison.ternary" rel="noopener noreferrer"&gt;PHP documentation&lt;/a&gt; suggests that is the most likely use case.&lt;br&gt;
&lt;code&gt;Else&lt;/code&gt; isn't that bad that it needs to be avoided by any means.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;Use tuple notation: [Controller::class, 'method']&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This assumes the controller is very likely to have constructor dependency injection, while it is better to use method dependency injection. &lt;br&gt;
The method dependency injection makes it also possible to use &lt;code&gt;new Controller()-&amp;gt;method(...)&lt;/code&gt; which is more robust than the method as a string.&lt;br&gt;
In the few cases constructor dependency injection is the best solution, the tuple notation can be used.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;Stick to CRUD methods (index, create, store, show, edit, update, destroy)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I understand the rule from a consistency perspective. On the other hand it is very restrictive. &lt;br&gt;
When the controllers are only CRUD vessels, is Laravel the right solution?&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;Use config() helper, avoid env() outside config files&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Solid rule!&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;Put output before processing an item (easier debugging):&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$items&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Item&lt;/span&gt; &lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Processing item id `&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;processItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Processed &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$items&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; items."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It assumes there is a build-in way to add debug information. As far as I know that is not default. &lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;Class constants also use PascalCase:&lt;/p&gt;


&lt;/blockquote&gt;

&lt;p&gt;This is not compatible with &lt;a href="https://www.php-fig.org/psr/psr-2/#a2-survey-legend" rel="noopener noreferrer"&gt;PSR-2&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;An agent skill is hard to get right because different models can interpret information in another way. &lt;br&gt;
The skills will work with the model(s) Spatie is using, and that is why some of the guidelines may look a bit out of place.&lt;/p&gt;

&lt;p&gt;Like with any other guidelines, they are opinionated. And because we all have our own opinions there are parts where we don't agree.&lt;br&gt;
But the nice thing about agent skills is that they are easy to change to your own liking. &lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
    </item>
    <item>
      <title>Are routing files a thing of the past in PHP?</title>
      <dc:creator>david duymelinck</dc:creator>
      <pubDate>Fri, 17 Apr 2026 03:42:09 +0000</pubDate>
      <link>https://dev.to/xwero/are-routing-files-a-thing-of-the-past-in-php-5eg0</link>
      <guid>https://dev.to/xwero/are-routing-files-a-thing-of-the-past-in-php-5eg0</guid>
      <description>&lt;p&gt;The one link that got my attention in this weeks The Weekly Drop was &lt;a href="https://www.drupal.org/node/3324758" rel="noopener noreferrer"&gt;PHP Attributes Can Be Used for Route Definition and Discovery&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a long time user of Symfony this feels like Drupal is catching up, and I'm all for it. The Drupal developers are really embracing &lt;a href="https://www.php.net/manual/en/language.attributes.overview.php" rel="noopener noreferrer"&gt;attributes&lt;/a&gt;. The first time I noticed it is when they started using it for &lt;a href="https://api.drupal.org/api/drupal/core%21core.api.php/group/hooks/11.x" rel="noopener noreferrer"&gt;hooks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next to the &lt;code&gt;Route&lt;/code&gt; attribute the change also allows the use of invokable controllers. From the Drupal article:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Drupal\router_test\Controller&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Drupal\Core\Controller\ControllerBase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\Routing\Annotation\Route&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cd"&gt;/**
 * Test controller.
 */&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s1"&gt;'/test_class_attribute'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'test_class_attribute'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;requirements&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'_access'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'TRUE'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestClassAttribute&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;ControllerBase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="cd"&gt;/**
   * Provides test content.
   */&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__invoke&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'#markup'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Testing __invoke() with a Route attribute on the class'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I see quite a few people using single route controllers. And to me it doesn't make sense having to name a method when PHP provides a way to invoke a class with a single public method.&lt;/p&gt;

&lt;p&gt;With Symfony, &lt;a href="https://tempestphp.com/" rel="noopener noreferrer"&gt;Tempest&lt;/a&gt; and now Drupal offering attribute based routing, it looks like Laravel is the biggest framework that doesn't uses attributes for routing.&lt;br&gt;
Laravel already has &lt;a href="https://laravel.com/docs/13.x/controllers#middleware-attributes" rel="noopener noreferrer"&gt;middleware attributes&lt;/a&gt;. This is one of the parts of the routing that is heavily used. &lt;/p&gt;

&lt;p&gt;There is a &lt;a href="https://github.com/spatie/laravel-route-attributes" rel="noopener noreferrer"&gt;third party package&lt;/a&gt; that makes it possible to use attributes for routing.&lt;br&gt;
And you can use it with the Laravel middleware attributes.&lt;br&gt;
This shows it could be a core feature.&lt;/p&gt;

&lt;p&gt;It would be amazing when the frameworks could create a standard for the routing attributes. This would make it possible to use controllers in multiple PHP solutions with a few changes or a framework discovery feature.&lt;/p&gt;

&lt;p&gt;What is your route attribute use? When you use a router config file, what is stopping you from using attributes?&lt;/p&gt;

</description>
      <category>symfony</category>
      <category>laravel</category>
      <category>drupal</category>
      <category>discuss</category>
    </item>
  </channel>
</rss>
