<?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: Stiven Llupa</title>
    <description>The latest articles on DEV Community by Stiven Llupa (@stivenllupa).</description>
    <link>https://dev.to/stivenllupa</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3790338%2Fba1429ad-55df-4fdf-b2a7-10f1ffe097c1.jpg</url>
      <title>DEV Community: Stiven Llupa</title>
      <link>https://dev.to/stivenllupa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/stivenllupa"/>
    <language>en</language>
    <item>
      <title>I saw a picture on the internet!</title>
      <dc:creator>Stiven Llupa</dc:creator>
      <pubDate>Thu, 22 Oct 2026 08:50:40 +0000</pubDate>
      <link>https://dev.to/stivenllupa/i-saw-a-picture-on-the-internet-3n8i</link>
      <guid>https://dev.to/stivenllupa/i-saw-a-picture-on-the-internet-3n8i</guid>
      <description>&lt;p&gt;Something happened yesterday that I want to talk about: the PHP security release. Let me start by saying, if you haven't updated to 8.5.12, 8.4.27, 8.3.36 or 8.2.35, go do that first. This post will still be here.&lt;/p&gt;

&lt;p&gt;There were posts in relevant channels, at least they seemed relevant enough, about an upcoming PHP security release. This was an &lt;strong&gt;important&lt;/strong&gt; one, so the PHP Foundation wanted people to be ready to upgrade, and I assume, not simply treat it as "&lt;em&gt;we will do it when we do it&lt;/em&gt;". That part was fine. Several things that happened after it were worrisome, and I want to talk about them.&lt;/p&gt;

&lt;p&gt;First, hackers proving the exploit over &lt;strong&gt;the internet&lt;/strong&gt;. The CVE numbers leaked a day early, with a CVSS of 10.0 attached (the real score is 9.2), and a proof-of-concept video went up and came down, but not before it got copied around. This caused panic, and then social media, or bots, or both, started to share how to "fix" this.&lt;/p&gt;

&lt;p&gt;Eventually there was the post from &lt;a href="https://cert.europa.eu/publications/security-advisories/2026-018" rel="noopener noreferrer"&gt;CERT-EU itself&lt;/a&gt;. Whether it was because of the social media momentum, or because they knew something we did not know, I don't know. But what they actually wrote was: patch immediately, and &lt;em&gt;where that is not feasible&lt;/em&gt;, consider isolating internet-facing applications until the fix is in. Somehow that got distilled to "&lt;strong&gt;go offline&lt;/strong&gt;". It was so bad that even governmental services here and there were not working. (Remember this, it is important for later.)&lt;/p&gt;

&lt;p&gt;Then came the AI wave. I don't know how to name it exactly, so I am sticking with that. Panicked elephants, end-of-the-world style pictures, the works. And this while other critical security issues were also released yesterday, for example this one in &lt;a href="https://thehackernews.com/2026/10/critical-gitlab-flaw-cvss-100-could.html" rel="noopener noreferrer"&gt;GitLab&lt;/a&gt;. That one is an actual CVSS 10.0, unauthenticated, every self-managed version back to 2014, and no workaround. The AI wave propelled the panic and fear, because something really bad is happening in PHP so we all should panic &lt;strong&gt;EVEN MORE&lt;/strong&gt;. How I experienced it: some RCEs are worse than others, so it is totally fine to accept some RCEs, or faulty signatures, as long as we panic over THIS one RCE!&lt;/p&gt;

&lt;p&gt;To be fair, the PHP bugs were serious. Unauthenticated RCE is no joke, and patching fast was the right call. But the attention we gave each issue, before we even knew what the issues were, had nothing to do with the actual risk. It had to do with the noise.&lt;/p&gt;

&lt;p&gt;Since 2020 or 2021, I have had the chance to do regular security trainings, and in 2023/2024 I also did more blue-team type training. After that, a refresher yearly. I don't want to bore you with the details of my career, but, like, the one thing they teach you all the time is: do not panic, and do not believe anyone that tries to instill a sense of urgency in you. So what did virtually 99% of anyone I know and "saw" online do? &lt;strong&gt;Panic&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I don't want to focus too much on the actual release process and how long it took for the patched version to propagate through all the different package registries, or containers, or the other ways of pulling this. I believe yesterday was a learning moment for everyone. What I found interesting were two posts about the "post mortem" of this release.&lt;/p&gt;

&lt;p&gt;The first one is from the &lt;a href="https://thephp.foundation/blog/2026/10/21/todays-php-security-release-and-how-were-strengthening-php-security/" rel="noopener noreferrer"&gt;PHP Foundation&lt;/a&gt;. This entire post is excellent if you have not read it. It touches more topics than a simple "there's news". One thing that stood out to me: &lt;em&gt;one of the providers that was part of the coordinated release broke the embargo more than 6 hours before the planned release&lt;/em&gt;. (Second thing that is important for later.)&lt;/p&gt;

&lt;p&gt;The second post that I highly recommend is from &lt;a href="https://daubois.dev/blog/cve-2026-109618-php2shell-rumors/" rel="noopener noreferrer"&gt;Alexandre Dubois&lt;/a&gt;, who is in the security team. Alexandre saw firsthand how "internet points" can change an entire coordinated release schedule. He also showed us that the trending posts about changing our session ini settings were conflating bugs. The flag everyone was sharing belongs to the &lt;em&gt;other&lt;/em&gt; CVE, and the stopgap the advisory actually puts first barely showed up anywhere. He mentions there was some good pre-release advice too, but at that point, &lt;em&gt;we the people&lt;/em&gt; were just shooting darts in the dark. What we were effectively doing was opening up our deployments to manipulation from unverified sources. (Third important thing.)&lt;/p&gt;

&lt;p&gt;Ok, now my main complaint-as-advice from what I experienced on the 21st of October 2026: &lt;em&gt;just because someone on the internet said so, it does not mean it is true&lt;/em&gt; has never been more true.&lt;/p&gt;

&lt;p&gt;What I learned if I were a malicious actor is that what I need is a good-enough bot farm, probably an insider at places that do not respect embargoes, and I can achieve a lot:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;first&lt;/strong&gt;, I can start a trend so everyone runs around ignoring some other CVE that I have a better grip on exploiting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;second&lt;/strong&gt;, I can try to get embargoes broken. This gives me access to a LOT of services that simply cannot get a patched version, because they are not in the provider's (offender's) chain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;third&lt;/strong&gt;, I can even engage in proxy wars by limiting governmental functions, or annoy some population elsewhere and blame it on the followers of the party I do not support.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;fourth&lt;/strong&gt;: probably more, but I don't really like being a red-team player.&lt;/p&gt;

&lt;p&gt;So next time: patch, yes. Fast, yes. But read the actual advisory, ask which bug a "fix" is fixing, and when someone tries really hard to make you hurry, that is exactly the moment to slow down.&lt;/p&gt;

</description>
      <category>security</category>
      <category>php</category>
      <category>webdev</category>
      <category>socialmedia</category>
    </item>
    <item>
      <title>The Scenic Route to TYPO3 DevDays</title>
      <dc:creator>Stiven Llupa</dc:creator>
      <pubDate>Tue, 11 Aug 2026 08:16:24 +0000</pubDate>
      <link>https://dev.to/stivenllupa/the-scenic-route-to-typo3-devdays-2kba</link>
      <guid>https://dev.to/stivenllupa/the-scenic-route-to-typo3-devdays-2kba</guid>
      <description>&lt;p&gt;Maps gave me two options to the conference venue: walk alongside the highway, or cut through "the park."&lt;/p&gt;

&lt;p&gt;Naturally, I picked the park. Who wouldn't with this in its entry? &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F27g6jvg85l2di9yw1yjx.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F27g6jvg85l2di9yw1yjx.jpg" alt=" " width="800" height="602"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What Maps called a park was, in fact, a forest 🌲. So there I was, conference-ready, laptop bag slung over my shoulder, nice shoes and all, strolling through actual woods like I was auditioning for a hiking ad. 🚶&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgvpi3lpuczzdq2lw98e3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgvpi3lpuczzdq2lw98e3.jpg" alt=" " width="760" height="1009"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Further in, the path was flooded. Someone before me had improvised a bridge out of what I can only describe as twigs (they were, structurally speaking, not twigs, and I chose not to think too hard about that while crossing).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F50fe9989obtdiu7ceiy2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F50fe9989obtdiu7ceiy2.jpg" alt=" " width="760" height="1009"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, just when I was sure I'd finally reach the venue, the trees opened up onto a cornfield. A full, sprawling cornfield. I stood there for a second, looked around, and just laughed. &lt;em&gt;This is awesome&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnxonoz9e8at4ptueausl.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnxonoz9e8at4ptueausl.jpg" alt=" " width="800" height="602"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And there it was: the conference center, right on the other side.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F64mjzb9me4uwh4vbljgn.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F64mjzb9me4uwh4vbljgn.jpg" alt=" " width="800" height="602"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A colleague and I had been invited over from the Symfony community and TYPO3 has adopted a good chunk of Symfony components under the hood, so the overlap made sense. TYPO3 DevDays itself kept that same energy going: warm, welcoming, genuinely good vibes from start to finish. But the thing that made me think more than the forest, the bridge, or the corn.&lt;/p&gt;

&lt;p&gt;It was how many speakers, across totally different talks, kept circling back to the same idea: &lt;strong&gt;build for your users&lt;/strong&gt;. Not for the framework, not for the demo, not for what's technically impressive, but for the actual person on the other end.&lt;/p&gt;

&lt;p&gt;Now that the walk and conference has finished, I am stuck with two things. First, how crazy it was to walk through a forest to reach a tech-con 😅 and second, the importance of end user feels like it is always implied, but never really actively worked on. (&lt;em&gt;I am look at you 👀, stake-holders&lt;/em&gt; 🧑‍💼) &lt;/p&gt;

</description>
      <category>typo3</category>
      <category>symfony</category>
      <category>adventure</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Defensive Programming can be very easy with Symfony HttpClient</title>
      <dc:creator>Stiven Llupa</dc:creator>
      <pubDate>Sat, 28 Feb 2026 18:37:39 +0000</pubDate>
      <link>https://dev.to/stivenllupa/defensive-programming-can-be-very-easy-with-symfony-httpclient-25g8</link>
      <guid>https://dev.to/stivenllupa/defensive-programming-can-be-very-easy-with-symfony-httpclient-25g8</guid>
      <description>&lt;p&gt;If your app lets users submit URLs that you then fetch, for link previews, webhooks, or RSS feeds, you have a serious security problem waiting to happen.&lt;/p&gt;

&lt;p&gt;An attacker can submit something like &lt;code&gt;http://127.0.0.1/admin&lt;/code&gt; or &lt;code&gt;http://169.254.169.254&lt;/code&gt; (AWS metadata endpoint) and your server will happily fetch it. &lt;/p&gt;

&lt;p&gt;That's called a &lt;strong&gt;S&lt;/strong&gt;erver-&lt;strong&gt;S&lt;/strong&gt;ide &lt;strong&gt;R&lt;/strong&gt;equest &lt;strong&gt;F&lt;/strong&gt;orgery attack, or &lt;strong&gt;SSRF&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Symfony has a built-in solution: &lt;a href="https://github.com/symfony/symfony/blob/8.1/src/Symfony/Component/HttpClient/NoPrivateNetworkHttpClient.php" rel="noopener noreferrer"&gt;NoPrivateNetworkHttpClient&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So your code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LinkPreviewController&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;HttpClientInterface&lt;/span&gt; &lt;span class="nv"&gt;$client&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="na"&gt;#[Route('/api/preview', methods: ['POST'])]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;preview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;JsonResponse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getPayload&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;getString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'url'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$url&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;JsonResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getContent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="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;Becomes:&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;LinkPreviewController&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;HttpClientInterface&lt;/span&gt; &lt;span class="nv"&gt;$safeClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="kt"&gt;HttpClientInterface&lt;/span&gt; &lt;span class="nv"&gt;$client&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;safeClient&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;NoPrivateNetworkHttpClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nv"&gt;$client&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="na"&gt;#[Route('/api/preview', methods: ['POST'])]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;preview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;JsonResponse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getPayload&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;getString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'url'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;safeClient&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$url&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;JsonResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getContent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's a decorator, so you just wrap your existing client &amp;amp; no other changes needed. Follow the &lt;a href="https://symfony.com/doc/current/http_client.html#ssrf-server-side-request-forgery-handling" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; to set your specific IPs or IP ranges that you want to disallow.&lt;/p&gt;

&lt;p&gt;If you fetch user-submitted URLs and you're not using this, you should be.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtube.com/shorts/V3qjRbszjW8" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;Watch in on YouTube&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>php</category>
      <category>symfony</category>
      <category>security</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Country codes and regional differences</title>
      <dc:creator>Stiven Llupa</dc:creator>
      <pubDate>Sat, 28 Feb 2026 09:38:49 +0000</pubDate>
      <link>https://dev.to/stivenllupa/country-codes-and-regional-differences-5fmh</link>
      <guid>https://dev.to/stivenllupa/country-codes-and-regional-differences-5fmh</guid>
      <description>&lt;p&gt;Here's a little-known Symfony trick for apps that need to support Kosovo as a country.&lt;/p&gt;

&lt;p&gt;Kosovo uses the code &lt;code&gt;XK&lt;/code&gt;, but that's not an official ISO 3166 code. It is what is called a &lt;strong&gt;user-assigned&lt;/strong&gt; code, meaning Symfony Intl component doesn't include it by default.&lt;/p&gt;

&lt;p&gt;So when you call &lt;code&gt;Countries::getNames()&lt;/code&gt; from the Symfony Intl component, Kosovo simply won't appear.&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;Symfony\Component\Intl\Countries&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$countries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Countries&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;getNames&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$countries&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'XK'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// false&lt;/span&gt;

&lt;span class="nc"&gt;Countries&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'XK'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Throws MissingResourceException&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fix? Just set this environment variable in your .env file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# .env&lt;/span&gt;

&lt;span class="nv"&gt;SYMFONY_INTL_WITH_USER_ASSIGNED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Symfony will now also recognize &lt;code&gt;XK&lt;/code&gt;, &lt;code&gt;XKK&lt;/code&gt;, and numeric code &lt;code&gt;983&lt;/code&gt; for Kosovo.&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;Symfony\Component\Intl\Countries&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;Countries&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'XK'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 'Kosovo'&lt;/span&gt;

&lt;span class="nc"&gt;Countries&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;getAlpha2Code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'XKK'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// XK&lt;/span&gt;
&lt;span class="nc"&gt;Countries&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;getAlpha3Code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'XK'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// XKK  &lt;/span&gt;
&lt;span class="nc"&gt;Countries&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;getNumericCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'XK'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// 983&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A one-liner that saves you from building a workaround. Super useful if you're building apps for any region that needs user-assigned country codes.&lt;/p&gt;

</description>
      <category>symfony</category>
      <category>php</category>
      <category>icu</category>
      <category>dx</category>
    </item>
    <item>
      <title>Stop writing the same regex for #[Route]</title>
      <dc:creator>Stiven Llupa</dc:creator>
      <pubDate>Fri, 27 Feb 2026 18:08:10 +0000</pubDate>
      <link>https://dev.to/stivenllupa/stop-writing-the-same-regex-for-route-o2c</link>
      <guid>https://dev.to/stivenllupa/stop-writing-the-same-regex-for-route-o2c</guid>
      <description>&lt;p&gt;Did you know Symfony ships with a built-in class full of pre-defined route requirement patterns?&lt;/p&gt;

&lt;p&gt;It's called &lt;a href="https://github.com/symfony/symfony/blob/7.4/src/Symfony/Component/Routing/Requirement/Requirement.php" rel="noopener noreferrer"&gt;Requirement&lt;/a&gt;, and it lives in &lt;code&gt;Symfony\Component\Routing\Requirement&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Instead of writing your own regex for common route parameters like UUIDs, slugs, date formats, or locale codes, you &lt;em&gt;can&lt;/em&gt; just reference a constant.&lt;/p&gt;

&lt;p&gt;So instead of this mess in your route attribute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="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;'/users/{id}'&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;'id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'[0-9a-f]{8}-[0-9a-f]{4}-'&lt;/span&gt;
          &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'[0-9a-f]{4}-[0-9a-f]{4}-'&lt;/span&gt;
          &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'[0-9a-f]{12}'&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;show&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;$id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="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;'/users/{id}'&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;'id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Requirement&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;UUID&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;show&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;$id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="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;'/blog/{slug}'&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;'slug'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'[a-z0-9]+(?:-[a-z0-9]+)*'&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;post&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;$slug&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="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;'/blog/{slug}'&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;'slug'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Requirement&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ASCII_SLUG&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;post&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;$slug&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It includes constants for all UUID formats, common date, ASCII slugs, and your usual suspects.&lt;/p&gt;

&lt;p&gt;Stop rewriting the same regex. Symfony already did it for you.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtube.com/shorts/OatqlyRgNSg" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;Watch it on YouTube&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>php</category>
      <category>symfony</category>
      <category>dx</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
