PHP - BlogFlockAll things PHP2025-07-13T07:31:33.788ZBlogFlockPhpStorm : The IDE that empowers PHP developers | The JetBrains Blog, Exakat, Rob Allen, Blog entries :: mwop.netUsing the 1Password CLI in a script - Rob Allenhttps://akrabat.com/?p=74072025-07-08T09:00:00.000Z<p>I'm currently writing a script that notarises a macOS CLI app which needs to access a password. Rather than put it in an environment variable, I thought I'd use the 1Password CLI. This is the first time I've used it, so these are my notes.</p>
<p>The 1Password CLI tool is call <tt>op</tt>. I installed it via Homebrew with:</p>
<pre>brew install 1password-cli</pre>
<h2>Sign in</h2>
<p>You need to sign in. </p>
<pre>op signin</pre>
<p>As I have multiple accounts as various clients have shared access to specific vaults, it asks me which account I want to sign in. To save this step, you can set the <tt>OP_ACCOUNT</tt> environment variable:</p>
<pre>export OP_ACCOUNT=my.1password.com</pre>
<p>Alternatively, use the <tt>--account</tt> parameter.</p>
<p>You get a dialog box where for me, I use TouchID to sign in.</p>
<p><tt>op signin</tt> is idempotent so is a no-op if the Terminal is already signed in.</p>
<h2>Access data</h2>
<p>There are multiple ways to retrieve the data from a 1Password item.</p>
<h3><tt style="color:inherit">op item get</tt></h3>
<p>Use <tt>op item get "<item>" --field "<fieldname>"</tt> to get a specific field. e.g</p>
<pre>op item get "Apple App Notarisation" --field "username"</pre>
<p>The <tt><item></tt> can be the name of the item or its id. e.g. something like <tt>dajka2z5l57m4p43s6bapd3eo4</tt></p>
<p>Note, that for a password, you also need to pass in <tt>--reveal</tt>.</p>
<p>As I'm writing a script, I assign to a variable:</p>
<pre>
APPLE_ID=$(op item get "Apple App Notarisation" --field username)
APP_SPECIFIC_PASSWORD=$(op item get "Apple App Notarisation" --field password --reveal)
TEAM_ID=$(op item get "Apple App Notarisation" --field team_id)
</pre>
<p>Alternatively, you can get back multiple fields in one go by providing a list of common separated fields:</p>
<pre>
FIELDS=$(op item get "Apple App Notarisation" --fields username,password,team_id --reveal)
IFS=',' read -r APPLE_ID APP_SPECIFIC_PASSWORD TEAM_ID <<< "$FIELDS"
</pre>
<h3><tt style="color:inherit">op read</tt></h3>
<p>You can also use the <tt>read parameter</tt> which takes a URL-style path:</p>
<pre>
op read op://<vault>/<item>/<field>
</pre>
<p>Use <tt>op vault list</tt> to view the list of vault names and you don't need <tt>--reveal</tt> for passwords. </p>
<p>For my case, I can use:</p>
<pre>
APPLE_ID=$(read "op://Private/Apple App Notarisation/username")
APP_SPECIFIC_PASSWORD=$(op read "op://Private/Apple App Notarisation/password")
TEAM_ID=$(op read "op://Private/Apple App Notarisation/team_id")
</pre>
<h3>Format as JSON</h3>
<p>You can also get the entire item in JSON using:</p>
<pre>op item get "Apple App Notarisation" --format json</pre>
<p>Then use <a href="https://jqlang.org/"><tt>jq</tt></a> to extract what you need. e.g to print the username and password you could do:</p>
<pre>op item get "Apple App Notarisation" --format json | jq -r '
.fields[] | select(.label=="username" or .label=="password") | "\(.label): \(.value)"
'</pre>
<h3>That's it</h3>
<p>That's it. Very simple to put into a script and keep my password secure.</p>
Infinite loops in PHP - Exakathttps://www.exakat.io/?p=158462025-07-03T17:58:12.000Z<figure id="attachment_15848" aria-describedby="caption-attachment-15848" style="width: 300px" class="wp-caption alignleft"><a href="https://www.exakat.io/wp-content/uploads/2025/07/infinite.320.jpg"><img fetchpriority="high" decoding="async" class="size-medium wp-image-15848" src="https://www.exakat.io/wp-content/uploads/2025/07/infinite.320-300x300.jpg" alt="" width="300" height="300" srcset="https://www.exakat.io/wp-content/uploads/2025/07/infinite.320-150x150@2x.jpg 300w, https://www.exakat.io/wp-content/uploads/2025/07/infinite.320-150x150.jpg 150w, https://www.exakat.io/wp-content/uploads/2025/07/infinite.320-100x100.jpg 100w, https://www.exakat.io/wp-content/uploads/2025/07/infinite.320.jpg 320w" sizes="(max-width: 300px) 100vw, 300px" /></a><figcaption id="caption-attachment-15848" class="wp-caption-text">OLYMPUS DIGITAL CAMERA</figcaption></figure>
<h1 id="toc_0">Infinite loops in PHP</h1>
<p>Sometimes, they are demonized, and sometimes, they are useful. Infinite loops come in a variety of options in PHP, so let’s put them neatly on a shelf, with all theirs variations.</p>
<h2 id="toc_1">Classic <code>while (true)</code></h2>
<p>To make an infinite loop, the most simple is to take a loop, and make it unconditional. The poster child for this is <code>while (true)</code>.</p>
<div>
<pre class="brush: php; title: ; notranslate">
<?php
$i = 0;
while (true) {
echo ++$i.PHP_EOL;
}
?>
</pre>
</div>
<h2 id="toc_2">Using <code>do...while(true)</code></h2>
<p>Obviously, <code>do...while</code> can do the same than <code>while</code>, but it is seldom mentionned anywhere. Indeed, <code>do...while</code> is roughly used 10 times less than <code>while</code>.</p>
<p>The main difference is that the loop will be executed at least one, but with infinite loop, what differences does this make?</p>
<div>
<pre class="brush: php; title: ; notranslate">
<?php
$i = 0;
do {
echo ++$i.PHP_EOL;
} while (true);
?>
</pre>
</div>
<h2 id="toc_3">With <code>for (;;)</code></h2>
<p>Another big star of the infinite loops is the empty <code>for()</code>. Here, the loop uses 3 expressions, which are initialization, terminaison and increment. Usually, removing terminaison or increment lead both to infinite loop.</p>
<p>In the end <code>for (;;)</code> is a lot more readable.</p>
<div>
<pre class="brush: php; title: ; notranslate">
<?php
$i = 0;
for (;;) {
echo ++$i.PHP_EOL;
}
for ($i = 0;;) {
echo ++$i.PHP_EOL;
}
for ($i = 0;;++$i) {
echo $i.PHP_EOL;
}
?>
</pre>
</div>
<h2 id="toc_4">Using a Generator with <code>foreach()</code></h2>
<p>Let’s move to <code>foreach()</code>. This one is naturally bounded: <code>foreach()</code> reads all the values in the provided source. This means that no source build on an array can be infinite: PHP will never build an infinite array before running out of memory.</p>
<p>So, we can use a generator. Let’s see how this works:</p>
<div>
<pre class="brush: php; title: ; notranslate">
<?php
function foo() {
$i = 0;
while (true) {
yield $i++;
}
}
foreach (foo() as $i) {
echo $i . PHP_EOL;
}
?>
</pre>
</div>
<p>To be honest, this is a bit cheating. There is a hidden <code>while(true)</code> hidden in the generator, which is actually creating the infinite loop. Let’s see if we can do better.</p>
<h2 id="toc_5">Using an inifinite Generator with <code>foreach()</code></h2>
<p>To get rid of the <code>while(true)</code> in the generator, we can rely on our good old friend <code>yield from</code>. While <code>yield</code> emits one value, <code>yield from</code> can do the same from an array, or another generator. We just need to get an infinite number of generators.</p>
<p>Or, we can use the same one, and pass it an argument. It is lesser known that generators can take arguments, but this works. Here, the second generator is initialized with the previous last value. This builds an infinite recursion.</p>
<div>
<pre class="brush: php; title: ; notranslate">
<?php
function goo($i = 0) {
yield $i;
yield from goo($i + 1);
}
foreach (goo() as $i) {
echo $i . PHP_EOL;
}
?>
</pre>
</div>
<p>Compared to the previous examples, it certainly less efficient, as the recursive calls are creation nested execution contexts. We might exhaust the memory before reaching the infinite…. (sic).</p>
<h2 id="toc_6">Infinite <code>foreach()</code> by itself</h2>
<p>One trick of <code>foreach()</code> is that it works on a copy of the initial array. This is a default security to make loops finite. But, <code>foreach()</code> also ensures that all the keys of the source array are actually used.</p>
<p>So, when the <code>foreach()</code> works with references, it actually switches to using the original array, as it may have to updates its values. Then, it runs over all the keys, but now, the keys may be changed by writing again in the same array. The trick is to create only new keys in the source array.</p>
<div>
<pre class="brush: php; title: ; notranslate">
<?php
$array = [1];
foreach ($array as &$value) {
$array[] = $value + 1;
echo $value . PHP_EOL;
}
?>
</pre>
</div>
<p>And also, to start with one value in the array, at least. Otherwise, PHP skips the whole loop.</p>
<h2 id="toc_7">Using <code>goto</code></h2>
<p>We could not avoid mentioning <code>goto</code> as a great tool to build infinite loops. After all, all the loops above are built on top of a hidden <code>goto</code>.</p>
<div>
<pre class="brush: php; title: ; notranslate">
<?php
$i = 0;
start:
echo ++$i.PHP_EOL;
goto start;
?>
</pre>
</div>
<p>Just don’t tell anyone I mentionned it, there are goto-haters in the wild.</p>
<h2 id="toc_8">Using a Recursive Function</h2>
<p>Finally, it is possible to skip entirely the loops by relying on recursive functions. Just skip the terminating condition.</p>
<p>We already ran into a variation of this, with the recursive generator. It was not the <code>yield</code> that was critical, but the recursion.</p>
<div>
<pre class="brush: php; title: ; notranslate">
<?php
function loop($i = 0) {
echo ++$i . PHP_EOL;
loop($i + 1);
}
loop();
?>
</pre>
</div>
<h1 id="toc_9">Infinite loop, the sky is the limit</h1>
<p>Infinite loops are useful, with indefinite waiting loops, such as event loops. This is when there is no limit to what may happen, so looping again and again is important.</p>
<p>In the other cases, it is better to know how to write an infinite loop. Not to use it, but very well to avoid running it in production.</p>
<p>The post <a href="https://www.exakat.io/infinite-loops-in-php/">Infinite loops in PHP</a> appeared first on <a href="https://www.exakat.io">Exakat</a>.</p>
Accessing my printer's web app remotely via Tailscale - Rob Allenhttps://akrabat.com/?p=74042025-07-01T09:00:00.000Z<p>We have an HP all-in-one scanner and printer that is on our local network. Recently, I was away from home and needed to reconfigure the scanning settings for unimportant reasons.</p>
<p>Usually, when I'm not in the office, I use <a href="https://tailscale.com/">Tailscale</a> to connect back to machines as required, but the printer isn't running Tailscale, so it's built-in web app isn't directly available. To solve this problem, I set up Tailscale subnet route on the Linux box I have in the office.</p>
<p>As this is Linux box, it was easy enough to SSH into it and I ran:</p>
<pre>
sudo tailscale set --advertise-routes=192.168.220./24
</pre>
<p>I can't remember why our local network uses the 192.168.220.0 range; I suspect it's related to a client's VPN config being overbearing, before I started jailing such clients in a VM.</p>
<p>The linux box now knows about the subnet route. To enable it, head over the <a href="https://login.tailscale.com/admin/machines">machines tab</a> of the Tailscale admin, find the machine in question and click the "…" button to find the "Edit route settings" item to authorise it.</p>
<p>Once this was done, Chrome on my remote Mac could access the printer's web app on https://192.168.220.24 and I did the admin required remotely.</p>
<p>I'm unclear if there'll be any issues leaving this route enabled all the time, as <a href="https://github.com/tailscale/tailscale/issues/1227">issue 1227</a> implies that everything local might get routed via my Linux box, so I disabled it after use and will enable it as an when I need it.</p>
FIxing Linux Tailscale exit node routing - Rob Allenhttps://akrabat.com/?p=74012025-06-24T09:00:00.000Z<p>I run a <a href="https://tailscale.com/">Tailscale</a> network so that remote computers can access local services. I also have a Linux box at home on that network that advertises itself as an <a href="https://tailscale.com/kb/1103/exit-nodes">exit node</a> and recently noticed that it wasn't working.</p>
<p>I had some time recently to sit down and work out what was going on. My initial suspicion was that it was DNS related as a cursory search brought up lots of results related to DNS. However, some quick tests with <tt>nslookup</tt> and <tt>dig</tt> showed that DNS was correctly resolving, so it seemed to be a routing issue.</p>
<p>Further searching led me to realise that my Linux box needs to masquerade the traffic. This can be done using:</p>
<pre>
sudo iptables -t nat -A POSTROUTING -o {network interface} -j MASQUERADE
</pre>
<p>I used <tt>ifconfig</tt> to look up my network interface which was <tt>enp3s0</tt> and then all was well. </p>
<p>I connected my Mac to the exit node from a remote location and could browse the web with my remote IP address correctly set to my home's IP address.</p>
<p>Given that this was working, I'm unclear what has changed such that this setting needed configuring. I have found <a href="https://github.com/tailscale/tailscale/issues/15708">issue 15708</a> which may be related so potentially a future Tailscale update will solve this. I don't rebook this box often, so maybe I set this flag before and forgot?</p>
<p>I've written it up here now though, so I can find it again if I need it!</p>
Discover Junie for PhpStorm: A Game-Changing AI Coding Agent for PHP Development - PhpStorm : The IDE that empowers PHP developers | The JetBrains Bloghttps://blog.jetbrains.com/?post_type=phpstorm&p=5770442025-06-23T09:20:03.000Z
<p>With the release of <a href="https://www.jetbrains.com/junie/" target="_blank" rel="noopener">Junie</a>, the AI coding agent by JetBrains, <a href="https://www.jetbrains.com/phpstorm/" target="_blank" rel="noopener">PhpStorm</a> has entered the realm of agentic IDEs. Now, the PhpStorm IDE doesn’t just provide classical developer productivity tools and AI assistance features, but can also do massive amounts of work for PHP developers autonomously.</p>
<p>Unlike other coding agent plugins, Junie is native to PhpStorm, which means it uses PhpStorm’s core features, such as source code navigation, project structure navigation, <em>Search Everywhere</em>, and code inspections to plan and execute multistep tasks and supervise the outcome. </p>
<p>You can <a href="https://www.jetbrains.com/help/junie/install-junie.html" target="_blank" rel="noopener">install</a> Junie like any other PhpStorm plugin and open it in the IDE by clicking the <strong><em> </em></strong><em>Junie</em> icon on the right-hand sidebar.</p>
<figure class="wp-block-image size-full"><img decoding="async" fetchpriority="high" width="2400" height="1236" src="https://blog.jetbrains.com/wp-content/uploads/2025/06/junie-laravel-demo.png" alt="" class="wp-image-577047"/></figure>
<p>In this blog post, we’ll follow Junie doing its work and see how it fits into your familiar PhpStorm workflow, while kicking off a whole new way of writing code.</p>
<h2 class="wp-block-heading">Starting with a clean Laravel project </h2>
<p>Let’s start with a clean Laravel project. There’s nothing here but the defaults, and what I want to do is add some functionality to it. So my prompt is:</p>
<p><code>Implement CRUD actions to manage books via web forms, also create and run a seeder to add 10 books into the database, write tests, and execute them. Use the Tailwind CSS form component library.</code></p>
<p><strong>💡Tip: </strong>To avoid follow-up clarifications where possible, provide Junie with detailed guidelines and instructions in the initial prompt. This helps minimize the number of requests sent to LLMs and optimize the token usage, which means you’re left with a higher <a href="https://youtrack.jetbrains.com/articles/SUPPORT-A-1860/What-is-the-quota-limit-for-different-JetBrains-AI-plans-AI-Free-AI-Trial-AI-Pro-AI-Ultimate" target="_blank" rel="noopener">JetBrains AI quota</a>!</p>
<p><strong>💡Tip: </strong>Select the <em>Think More/Smarter</em> checkbox to give Junie extra time to run and deliver deeper, more insightful results.</p>
<figure class="wp-block-image"><img decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2025/06/image-31.png" alt="" class="wp-image-577174"/></figure>
<h2 class="wp-block-heading">Watching Junie work</h2>
<p>When I run the prompt, Junie starts by creating a plan and then follows the proposed plan step by step, stopping at terminal commands for user confirmation.</p>
<figure class="wp-block-image"><img decoding="async" width="1600" height="978" src="https://blog.jetbrains.com/wp-content/uploads/2025/06/image-32.png" alt="" class="wp-image-577199"/></figure>
<p>As you can see, Junie is pretty transparent with what it does and which files it searches for, opens, or edits, reporting on what exactly is being performed at each step along the way. </p>
<p>While Junie is running, you can <strong>open Junie’s terminal</strong> to see the CLI command currently executed by the agent and the complete output history. You can even interact with the terminal while Junie is running some of the commands.</p>
<figure class="wp-block-image size-full"><img decoding="async" width="2412" height="1474" src="https://blog.jetbrains.com/wp-content/uploads/2025/06/junie-open-database.png" alt="" class="wp-image-577246"/></figure>
<p>When Junie is done, you can double-click the changed or added file to open it in PhpStorm’s diff viewer, locate and open the file itself in the editor, or roll back changes for the edited files. </p>
<figure class="wp-block-image size-full"><img decoding="async" loading="lazy" width="2400" height="1392" src="https://blog.jetbrains.com/wp-content/uploads/2025/06/junie-open-added-file.png" alt="" class="wp-image-577257"/></figure>
<p>From here, you can accept all changes, give Junie a follow-up task, or roll everything back if needed.</p>
<p>In our case, Junie created CRUD screens for managing books. It also wrote tests and executed them without any manual interference. There’s a new database seeder, and it was actually run, with 10 test books added to the database. </p>
<p>Exactly as instructed! </p>
<figure class="wp-block-image"><img decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2025/06/image-30.png" alt="" class="wp-image-577165"/></figure>
<figure class="wp-block-image size-full"><img decoding="async" loading="lazy" width="2412" height="982" src="https://blog.jetbrains.com/wp-content/uploads/2025/06/rows-added-to-database.png" alt="" class="wp-image-577268"/></figure>
<div class="buttons">
<div class="buttons__row">
<a href="https://plugins.jetbrains.com/plugin/26104-jetbrains-junie" class="btn" target="" rel="noopener">Try Junie</a>
</div>
</div>
<h2 class="wp-block-heading">The power of Junie guidelines</h2>
<p>In the initial prompt, I instructed Junie on which technologies to use. However, for more consistent outcomes, it’d be better to create a <code>.junie/guidelines.md</code> file in the project root and list all the preferred technologies, naming conventions, and coding standards there. Guidelines can also spare Junie from making irrelevant assumptions and minimize follow-ups or manual intervention.</p>
<figure class="wp-block-image size-full"><img decoding="async" loading="lazy" width="2406" height="1140" src="https://blog.jetbrains.com/wp-content/uploads/2025/06/project-guidelines.png" alt="" class="wp-image-577279"/></figure>
<p>You can compose the guidelines file yourself, ask Junie to generate project-specific guidelines on an existing project for you, or reuse the guidelines and rules that are specific to your framework or technology stack shared by other developers, for example, <a href="https://github.com/dcblogdev/laravel-junie/tree/main/src/docs" target="_blank" rel="noopener">Laravel guidelines by David Carr</a>. </p>
<p><strong>💡Tip: </strong>Use the <code>.aiignore</code> file to restrict Junie from accessing specific files and directories.</p>
<h2 class="wp-block-heading">Allowed terminal commands</h2>
<p>Additionally, in the example above, I manually confirmed all terminal commands run by Junie. However, I feel confident in allowing Junie to run any <code>php artisan</code> command without confirmation. To do so, I’ll add this type of command to the Junie allowlist in the IDE settings via <em>Settings | Tools | Junie | Action Allowlist</em> using the RegEx syntax.</p>
<p>Allowing all <code>php artisan</code> commands would look as follows:</p>
<figure class="wp-block-image size-full"><img decoding="async" loading="lazy" width="2406" height="1484" src="https://blog.jetbrains.com/wp-content/uploads/2025/06/allowlist.png" alt="" class="wp-image-577290"/></figure>
<p>To enable Junie to run all terminal commands without user confirmation, select the <em>Brave Mode</em> checkbox.</p>
<h2 class="wp-block-heading">Ask mode</h2>
<p>Junie comes with the agentic code mode selected by default. However, you can also chat with Junie in <em>Ask</em> mode, asking questions referencing your project context but without having your code changed. </p>
<figure class="wp-block-image size-full"><img decoding="async" loading="lazy" width="2400" height="916" src="https://blog.jetbrains.com/wp-content/uploads/2025/06/junie-ask-mode.png" alt="" class="wp-image-577301"/></figure>
<h2 class="wp-block-heading">Using Junie on existing projects</h2>
<p>We started with a clean project and let Junie do its thing. But what about existing projects? Junie is optimized for a profound understanding of the project context and working with large-scale repos and complicated tasks. PHP developers can use Junie to:</p>
<ul>
<li>Analyze and understand a large legacy codebase and update it to the latest versions of the framework and packages it uses.</li>
<li>Analyze the commit history to figure out issues in PHP or JavaScript code and fix them.</li>
<li>Analyze the codebase to detect weak spots and areas for improvement.</li>
<li>Explore new ways of implementing features or come up with quick proofs of concept.</li>
<li>Challenge Junie with something creative and investigate new ways of collaboration between human and agent!</li>
</ul>
<p></p>
<p>Junie is extremely powerful in delivering coding results, but ultimately, what it can do depends on the accuracy of the input it gets and the proficiency of the engineer using it. Do you have any impressive “<strong>Junified</strong>” use cases to share with us? We’d love to hear about them!</p>
<h2 class="wp-block-heading">What developers say</h2>
<p>Check out what other PHP developers have used Junie for:</p>
<ul>
<li><a href="https://youtu.be/YeR60hXjXaI?feature=shared" target="_blank" rel="noopener">NEW PhpStorm Junie AI VS Cursor: “Usable” Agent from JetBrains?</a> by Povilas Korop</li>
<li><a href="https://youtu.be/tfBS85Ksfag?feature=shared" target="_blank" rel="noopener">New AI Editor by JetBrains: Junie</a> by Nuno Maduro</li>
<li><a href="https://www.linkedin.com/pulse/first-impressions-junie-phpstorm-carlos-granados-ptuff/" target="_blank" rel="noopener">First impressions of Junie with PhpStorm</a> by Carlos Granados</li>
</ul>
<p>Junie is already making quite an impression in the PHP and Laravel communities. Here’s what early users say:</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Played around with Junie today. Very cool – and it does a particularly great job of showing you what it's doing. <a href="https://t.co/sSARLq0YrA" target="_blank">https://t.co/sSARLq0YrA</a> <a href="https://t.co/huDMG120wA" target="_blank">pic.twitter.com/huDMG120wA</a></p>— Jeffrey Way (@jeffrey_way) <a href="https://twitter.com/jeffrey_way/status/1921993344164532606?ref_src=twsrc%5Etfw" target="_blank" rel="noopener">May 12, 2025</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">I'm trying out Junie from <a href="https://twitter.com/jetbrains?ref_src=twsrc%5Etfw" target="_blank" rel="noopener">@jetbrains</a> in <a href="https://twitter.com/phpstorm?ref_src=twsrc%5Etfw" target="_blank" rel="noopener">@phpstorm</a>. I'm impressed.<br>The thing that stood out so far is that I didn't have to give it specific references to the files that need to be updated and it found all of them on its own. And at the end it asked to run the build process to…</p>— Mircea Sandu (@mircea_sandu) <a href="https://twitter.com/mircea_sandu/status/1921898378704605495?ref_src=twsrc%5Etfw" target="_blank" rel="noopener">May 12, 2025</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Gotta correct myself — Junie isn’t that slow when you consider it’s the best coding assistant I’ve tried so far. The results are insanely good. Amazing work, <a href="https://twitter.com/jetbrains?ref_src=twsrc%5Etfw" target="_blank" rel="noopener">@jetbrains</a>!</p>— Patrik Gmitter (@PatrikGmitter) <a href="https://twitter.com/PatrikGmitter/status/1926655403405742091?ref_src=twsrc%5Etfw" target="_blank" rel="noopener">May 25, 2025</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Finally got round to giving Junie in <a href="https://twitter.com/phpstorm?ref_src=twsrc%5Etfw" target="_blank" rel="noopener">@phpstorm</a> a go, it was a great help figuring out and then fixing some N+1 issues I was having in my application!</p>— Jon Purvis (@JonPurvis_) <a href="https://twitter.com/JonPurvis_/status/1924128405408117009?ref_src=twsrc%5Etfw" target="_blank" rel="noopener">May 18, 2025</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<div class="buttons">
<div class="buttons__row">
<a href="https://plugins.jetbrains.com/plugin/26104-jetbrains-junie" class="btn" target="" rel="noopener">Try Junie</a>
</div>
</div>
Renaming files with Hazel - Rob Allenhttps://akrabat.com/?p=73902025-06-17T10:00:00.000Z<p>I'm a member of a number of groups that publish a magazine, either paper-based or PDF. I prefer the PDF version, so download from the website and then move to the relevant directory.</p>
<p>Recently, I realised that I could use Hazel to do this for me.</p>
<p>To take one example, the filename of the PDF that I download is of the format <tt>PE-{issue number}-web{some random characters}.pdf</tt>, for example: <tt>PE-123-web9j45s3gd.pdf</tt>. There's sometimes a hyphen after <tt>web</tt>, but not always.</p>
<p>I want the filename to <tt>PE Magazine {issue number}</tt></p>
<p>The rule in Hazel looks like this:</p>
<picture><source
srcset="https://akrabat.com/wp-content/uploads/2025/06/2025hazel-rename-pe-mag-dark.png"
media="(prefers-color-scheme: dark)"
/><source
srcset="https://akrabat.com/wp-content/uploads/2025/06/2025hazel-rename-pe-mag-light.png"
media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)"
/><br />
<img decoding="async" src="https://akrabat.com/wp-content/uploads/2025/06/2025hazel-rename-pe-mag-light.png" loading="lazy" alt="Screenshot of Hazel rename rule" width="500"/>
</picture>
<h2>Matching the name</h2>
<p>Breaking it down, we detect that this is a file of interest with a <em>Name matches</em> rule. We can start with the literal string <tt>PE-</tt> as that's constant, but then we need to match the number and also give it a name so that we can refer to it later.</p>
<p>This is done using the "Custom Text" element. You can then click on it to edit its attributes where I set its name to "issue" and the pattern to "Number" which matches sequential digits.</p>
<picture><source
srcset="https://akrabat.com/wp-content/uploads/2025/06/2025hazel-match-attributes-dark.png"
media="(prefers-color-scheme: dark)"
/><source
srcset="https://akrabat.com/wp-content/uploads/2025/06/2025hazel-match-attributes-light.png"
media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)"
/><br />
<img decoding="async" src="https://akrabat.com/wp-content/uploads/2025/06/2025hazel-match-attributes-light.png" loading="lazy" alt="Screenshot of Hazel match attributes screen" class="border" width="300"/>
</picture>
<p>The rest of the name match is a hyphen followed by an "Anything" element as we don't care about any other characters.</p>
<p><tt>Renaming</tt></p>
<p>Now that we have we can use the <em>Rename with pattern</em> action with the literal "<tt>PE Magazine - </tt>" followed by our "issue" custom element that we created in the match rule, followed by the extension.</p>
<h2>Finishing it off</h2>
<p>Once the file has been renamed, there's a "Move to folder" action to move it the right place and then the folder is opened, so that I can grab the file if I need to.</p>
<p>That's it. Automating the rename and move means that I don't have to think about it again.</p>
How to make emojis in PHP - Exakathttps://www.exakat.io/?p=158182025-06-10T16:01:28.000Z<h1 id="toc_0"><a href="https://www.exakat.io/wp-content/uploads/2025/06/emoji.320.jpg"><img fetchpriority="high" decoding="async" class="alignleft size-medium wp-image-15819" src="https://www.exakat.io/wp-content/uploads/2025/06/emoji.320-300x300.jpg" alt="" width="300" height="300" srcset="https://www.exakat.io/wp-content/uploads/2025/06/emoji.320-150x150@2x.jpg 300w, https://www.exakat.io/wp-content/uploads/2025/06/emoji.320-150x150.jpg 150w, https://www.exakat.io/wp-content/uploads/2025/06/emoji.320-100x100.jpg 100w, https://www.exakat.io/wp-content/uploads/2025/06/emoji.320.jpg 320w" sizes="(max-width: 300px) 100vw, 300px" /></a>How to make emojis in PHP</h1>
<p>You might want to use emojis both in your online comments and your PHP code. The later, mostly to hander the former, although PHP handles emojis gracefully. That are the methods to make emojis in PHP?</p>
<h2 id="toc_1">Straight in the code</h2>
<p>PHP supports code savec in UTF-8. And emojis are also available in that character set so it is possible to use them directly in the code.</p>
<div>
<pre class="brush: php; title: ; notranslate">
<?php
echo '<img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f418.png" alt="🐘" class="wp-smiley" style="height: 1em; max-height: 1em;" />';
?>
</pre>
</div>
<p>This method is direct and visual. You may need an emoji encyclopedia, such as <a href="https://emojipedia.org/elephant">https://emojipedia.org/</a>, or <a href="https://emojis.wiki/">emojis.wiki</a>, where copy-paste is a great tool to select any emoji.</p>
<p>Yet, this all depends on the underlying text file to be always stored as UTF-8.</p>
<h2 id="toc_2">Using Escape Sequences</h2>
<p>It is possible to make emojis without relying on the UTF-8 support of the editor. PHP accepts escape sequences, which is a special format to represent long UTF-8 characters. All is in ASCII characters, so these emojis are more stable in the code. They are also no WISIWIG.</p>
<div>
<pre class="brush: php; title: ; notranslate">
<?php
// Method 1: Direct Unicode escape sequences
echo "\u{1F418}"; // <img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f418.png" alt="🐘" class="wp-smiley" style="height: 1em; max-height: 1em;" /> elephpant
?>
</pre>
</div>
<h2 id="toc_3">Using ext/mbstring</h2>
<p>mbstring is the PHP extension for Multi-bytes strings. It is installed on many PHP binary, as it is commonly used for UTF-8 manipulation. There is a dedicated function to create the emoji.</p>
<div>
<pre class="brush: php; title: ; notranslate">
<?php
print iconv('UTF-8', 'UCS-2BE', pack('H*', '1F418'); // <img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f418.png" alt="🐘" class="wp-smiley" style="height: 1em; max-height: 1em;" /> elephpant
?>
</pre>
</div>
<p>The codepoint has to be provided as an hexadecimal value, hence the <code>0x</code> prefix. In particular, the codepoint is mentionned in emoji or UTF-8 character references. It is also possible to pass the decimal or binary value of the codepoint, but it is less common.</p>
<h2 id="toc_4">Using iconv</h2>
<p>The other PHP extension to convert between encodings is iconv. The first step is to pack the codepoint into a 32 big endian integer, then to convert it from UCS-4 (big endian) to UTF-8.</p>
<div>
<pre class="brush: php; title: ; notranslate">
<?php
print iconv('UCS-4BE', 'UTF-8', pack('N', 0x1f418)); // <img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f418.png" alt="🐘" class="wp-smiley" style="height: 1em; max-height: 1em;" /> elephpant
?>
</pre>
</div>
<h2 id="toc_5">Using html_entity<em>_</em>decode()</h2>
<p>html_entity<em>_</em>decode() converts HTML sequences to characters. This is an alternative to using PHP escape-sequences,</p>
<div>
<pre class="brush: php; title: ; notranslate">
<?php
print html_entity_decode('&#' . 0x1F418 . ';', ENT_NOQUOTES, 'UTF-8'); // <img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f418.png" alt="🐘" class="wp-smiley" style="height: 1em; max-height: 1em;" /> elephpant
// space between dot and 0 is important
?>
</pre>
</div>
<h2 id="toc_6">Using a ready-made PHP component</h2>
<p>There are components that convert method calls, constants or escape sequences to emojis. This makes it easier to handle in case the emojis are dynamically managed, such as automatically added in titles or comments.</p>
<p>You can use <a href="https://packagist.org/packages/emojione/emojione">emojione/emojione</a> or <a href="https://packagist.org/packages/spatie/emoji"><em>spatie/emoji</em></a></p>
<div>
<pre class="brush: php; title: ; notranslate">
<?php
echo "<img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f418.png" alt="🐘" class="wp-smiley" style="height: 1em; max-height: 1em;" />"; // <img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f418.png" alt="🐘" class="wp-smiley" style="height: 1em; max-height: 1em;" /> elephpant
echo Emoji::elephant(); // <img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f418.png" alt="🐘" class="wp-smiley" style="height: 1em; max-height: 1em;" /> elephpant
echo Emoji::CHARACTER_ELEPHANT; // <img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f418.png" alt="🐘" class="wp-smiley" style="height: 1em; max-height: 1em;" /> elephpant
?>
</pre>
</div>
<h1 id="toc_7">Five ways to make emojis in PHP</h1>
<p>These are at least five ways to make emojis in PHP. Also, they may be used in PHP code itself, not just for string manipulations. This may be applicable for mathematical functions (function ∑(…$numbers)) or localisation (class 商品Manager). A lot of fun!</p>
<p>The post <a href="https://www.exakat.io/how-to-make-emojis-in-php/">How to make emojis in PHP</a> appeared first on <a href="https://www.exakat.io">Exakat</a>.</p>
How PhpStorm Helps Maintain PHP Open-Source Projects: Interviews and Real-World Examples - PhpStorm : The IDE that empowers PHP developers | The JetBrains Bloghttps://blog.jetbrains.com/?post_type=blog&p=5688642025-06-10T10:00:00.000Z
<figure class="wp-block-image size-full"><img decoding="async" fetchpriority="high" width="2560" height="1440" src="https://blog.jetbrains.com/wp-content/uploads/2025/05/PS-social-BlogFeatured-2560x1440-copy.png" alt="" class="wp-image-568886"/></figure>
<p>The PHP ecosystem is driven by passionate developers building tools that power everything from content management systems right the way through to testing libraries and database layers. Behind each project is a dedicated team working to modernize code, improve performance, and move the ecosystem forward.</p>
<p>The fact that many of these teams choose <a href="https://www.jetbrains.com/phpstorm/" target="_blank" rel="noopener">PhpStorm</a> to support their work is a source of pride for all of us at JetBrains and serves as proof of the positive impact on the wider PHP community of our <a href="https://www.jetbrains.com/phpstorm/buy/?section=discounts&billing=yearly" target="_blank" rel="noopener">free and discounted license program for open-source contributors</a>. This post highlights standout PHP projects and the people behind them. Whether they’re debugging complex systems or maintaining test suites, PhpStorm helps streamline workflows, reduce friction, and free up time for what matters most – building.</p>
<h2 class="wp-block-heading">PHPUnit</h2>
<p><a href="https://sebastian-bergmann.de/index.html" target="_blank" rel="noopener">Sebastian Bergmann</a> started <a href="https://github.com/sebastianbergmann/phpunit/" target="_blank" rel="noopener">PHPUnit</a> as a university project, prompted by a discussion with a professor who thought that a tool like JUnit could not be implemented for PHP. Since then, PHPUnit has been the backbone of PHP testing for over two decades and has shaped how PHP developers write and maintain tests. It remains the go-to testing framework for PHP projects of all sizes.</p>
<blockquote class="wp-block-quote">
<p>I tried every PHP IDE until I got my hands on PhpStorm – the first one that made me more productive, not less. It felt like home right away. I can’t imagine working without its code navigation and refactoring tools.</p>
<cite><em>— Sebastian Bergmann, PHPUnit creator</em></cite></blockquote>
<p>The latest release, PHPUnit 12, prioritizes code clarity. A major improvement is the clear distinction between test stubs and mock objects via dedicated APIs. This architectural shift simplifies test code maintenance and readability.</p>
<p>Looking ahead, PHPUnit will introduce support for the Open Test Reporting format – a modern, extensible alternative to JUnit XML. Initial support is planned for PHPUnit 12.2 (June 2025), with JUnit XML being deprecated in PHPUnit 13 and removed in PHPUnit 14.</p>
<h2 class="wp-block-heading">Doctrine DBAL</h2>
<p><a href="https://github.com/doctrine/dbal" target="_blank" rel="noopener">Doctrine DBAL</a> is a widely used database abstraction layer that gives PHP developers a portable, object-oriented API to interact with SQL databases. It powers a wide range of applications and frameworks across the PHP ecosystem.</p>
<blockquote class="wp-block-quote">
<p>I use PhpStorm daily to run PHPUnit tests locally with various configurations, interact with different databases, manage Docker containers, and run static analysis.</p>
<cite><em>— Sergei Morozov, Doctrine DBAL maintainer</em></cite></blockquote>
<p>While the project is mature and provides most of the essential functionality, ongoing work includes a fundamental rework of schema management, addressing limitations of the original architecture, and ensuring better support for evolving SQL standards and database platforms.</p>
<h2 class="wp-block-heading">CodeIgniter</h2>
<p><a href="https://github.com/codeigniter4/CodeIgniter4" target="_blank" rel="noopener">CodeIgniter</a> was created as a lightweight, high-performance PHP framework that prioritizes simplicity and developer freedom. It empowers developers to build on their own terms without rigid conventions – a core philosophy that continues to define its appeal.</p>
<p>CodeIgniter v4 maintains the core principles of its predecessor while embracing modern software development practices, such as robust testing and integration with tools like PHPStan, Psalm, and Rector.</p>
<blockquote class="wp-block-quote">
<p>One of CodeIgniter v4’s key strengths is its alignment with PHP best practices, allowing PhpStorm to work seamlessly out of the box – no extra plugins needed. The IDE instantly understands CodeIgniter’s patterns and conventions, offering intelligent code completion that streamlines development. This built-in compatibility creates an exceptionally productive experience for our contributors.</p>
<cite><em>— Matt Gatner, CodeIgniter contributor</em></cite></blockquote>
<p>The team continues to evolve CI4, focusing on performance, modularity, and a smooth developer experience. Upcoming releases aim to stabilize task and queue packages, expand the modular package library, and improve compatibility with the latest PHP versions – all while maintaining the project’s original vision.</p>
<h2 class="wp-block-heading">Joomla!</h2>
<p><a href="https://github.com/joomla/joomla-cms" target="_blank" rel="noopener">Joomla!</a> is a powerful open-source content management system sustained by a global community of volunteers. Its mission is to provide a multilingual, flexible, and secure platform that empowers individuals, small businesses, and nonprofits to publish and collaborate online – all without the steep learning curve of alternative systems.</p>
<blockquote class="wp-block-quote">
<p>PhpStorm’s static code analyzer helped me clean up docblocks and better manage the framework. It understands Joomla deeply, making development smoother.</p>
<cite>— <em>Hannes Papenberg, <em>Joomla Maintainer</em></em></cite></blockquote>
<blockquote class="wp-block-quote">
<p>PhpStorm shows me how files are connected, catches syntax errors early, and allows me to focus on actual client needs. It gives me a massive advantage over other web developers who don’t see the value of using it in their daily processes.</p>
<cite>— <em>Adam Melcher, <em>Joomla Contributor</em></em></cite></blockquote>
<blockquote class="wp-block-quote">
<p>As a Joomla core developer, PhpStorm has helped me in so many ways. The step debugger, which I use pretty much every single day, helps track down bugs, understand code flows, and generally, seeing what is going on under the hood is precious. The Joomla plugin adds an extra layer of usability as it understands the Joomla codebase and makes navigating the code a lot easier.</p>
<cite>— <em>Roland Dalmulder, <em>Joomla Contributor</em></em></cite></blockquote>
<p>Looking ahead, <a href="https://magazine.joomla.org/all-issues/july-2024/joomla-6,-what-s-in-it-for-you" target="_blank" rel="noopener">Joomla 6</a> is scheduled for release on October 14, 2025. It will bring further codebase modernization, better SEO tools, and a built-in health checker – continuing Joomla’s mission to make publishing on the web more inclusive and flexible.</p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<p>These projects represent just a small part of the global open-source effort, but they reflect the values we admire most: curiosity, craftsmanship, and care for the developer community.</p>
<p>While each project has its own focus, they all rely on consistent, powerful workflows to maintain high standards and move forward with clarity – and JetBrains is proud to support them in this endeavor. If you’re an open-source developer, you might be eligible for a free or discounted PhpStorm license – <a href="https://blog.jetbrains.com/phpstorm/2024/01/free-and-discounted-licenses-for-phpstorm-who-is-eligible/">read more</a> about the available options to see if you qualify.</p>
<p>What’s more, we’re also delighted to be able to host a celebration of the passion and progress of the PHP community in the form of <a href="https://lp.jetbrains.com/phpverse-2025/" target="_blank" rel="noopener">PHPverse 2025</a> – a free online event taking place on June 17, 2025, where PHP’s most influential voices will share their insights on the language’s evolution and its future. Join us for inspiring talks, discussions, Q&As, and a special PHP anniversary merch giveaway.</p>
<div class="buttons">
<div class="buttons__row">
<a href="https://lp.jetbrains.com/phpverse-2025/" class="btn" target="" rel="noopener">Sign Up for Free</a>
</div>
</div>
Updating SMTP relay in postfix - Rob Allenhttps://akrabat.com/?p=73832025-06-10T10:00:00.000Z<p>On a server that I help to maintain, it has <a href="https://www.postfix.org">postfix</a> installed for emailing results of cron jobs and other status updates. This was set up to relay through SendGrid as they had a 100 email per month plan and we send out significantly fewer than that.</p>
<p>Unfortunately, SendGrid are retiring their free plan, so I had to move to a new service and picked <a href="https://www.smtp2go.com">SMTP2GO</a> and so had to update the postfix configuration. As I didn't have this written down, I'm noting in here.</p>
<h2>Get SMTP details</h2>
<p>You need the <tt>SMTP hostname</tt>, <tt>SMTP username</tt> and <tt>SMTP password</tt>. SMTP2GO allows you to have multiple usernames and usefully allows you to rate limit each one individually, should you need that.</p>
<h2>Configure postfix</h2>
<h3>Firstly, update <tt>/etc/postfix/main.cf</tt></h3>
<p>Find <tt>relayhost</tt> and change to <tt>[mail.smtp2go.com]:587</tt>. I'm just updating, so the rest of the settings I left alone. </p>
<p>However, the relevant settings are:</p>
<pre>
relayhost = [mail.smtp2go.com]:587
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt
smtp_use_tls = yes
</pre>
<h3>Secondly, add the new SMTP user's credentials</h3>
<p>Edit <tt>/etc/postfix/sasl_passwd</tt>. This needs to be done as root as the permissions on this file are <tt>600</tt> and it's owned by <tt>root:root</tt>.</p>
<p>Add a new line with this format:</p>
<pre>
[mail.smtp2go.com]:587 {username}:{password}
</pre>
<p>Where <tt>{username}</tt> is the SMTP username and <tt>{password}</tt> is the SMTP password.</p>
<p>Save the file and then create the hash database:</p>
<pre>
sudo postmap /etc/postfix/sasl_passwd
</pre>
<h3>Restart postfix</h3>
<p>That's it, so just restart postfix with <tt>sudo service postfix restart</tt></p>
Global word count on Mac using Shortcuts - Rob Allenhttps://akrabat.com/?p=73612025-06-03T10:00:00.000Z<p>I've had a few cases recently when I wanted to know the number of words that I had written. To do this, I've copied the text to BBEdit which displays the word count in its status bar, but this is a bit of a faff.</p>
<p>I finally sat down and created a Shortcut for it that took 10 mins.</p>
<p>This is the shortcut:</p>
<picture><source
srcset="mhttps://akrabat.com/wp-content/uploads/2025/05/2025-05-rst2pdf.org-dark-1.pn"
media="(prefers-color-scheme: dark)"
/><source
srcset="https://akrabat.com/wp-content/uploads/2025/05/2025-05-word-count-shortcut-light.png"
media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)"
/><br />
<img decoding="async" src="https://akrabat.com/wp-content/uploads/2025/05/2025-05-word-count-shortcut-light.png" loading="lazy" alt="Screenshort of Word Count shortcut" width="600"/>
</picture>
<p>The idea is that I want to select the text and run the shortcut without having to copy to the clipboard, so to do this, I enable "Use as Quick Action" on the Services menu. I also assigned a global shortcut of <tt>⌃⌥⇧⌘W</tt> for convenience, though going to the Services menu isn't hard.</p>
<p>A Quick Action shortcut automatically gets a "Receive" input block, which I set to be just accept text input as it makes no sense to count words in other types of text. I also set it to look in the clipboard if there's no selection. Once we have input, we run it through the "Count" action and then pipe to a "Show Alert" so that the value can be seen. It's all quite easy.</p>
<p>That's it. I can now select text and press </p>
<pre>⌃⌥⇧⌘W</pre>
<p> and the number of words is displayed!</p>
<picture><source
srcset="https://akrabat.com/wp-content/uploads/2025/05/2025-05-wordcount-screenshot-dark.png"
media="(prefers-color-scheme: dark)"
/><source
srcset="https://akrabat.com/wp-content/uploads/2025/05/2025-05-wordcount-screenshot-light.png"
media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)"
/><br />
<img decoding="async" src="https://akrabat.com/wp-content/uploads/2025/05/2025-05-wordcount-screenshot-light.png" loading="lazy" alt="Screenshort of output of Word Count shortcut" width="300"/>
</picture>
PHP Annotated – May 2025 - PhpStorm : The IDE that empowers PHP developers | The JetBrains Bloghttps://blog.jetbrains.com/?post_type=phpstorm&p=5718852025-05-30T15:36:07.000Z<p><a href="https://blog.jetbrains.com/phpstorm/2025/05/php-annotated-may-2025/"><img decoding="async" class="alignnone size-full" src="https://blog.jetbrains.com/wp-content/uploads/2025/05/PS-social-BlogSocialShare-1280x720-2x.jpg" alt="PHP Annotated" width="900"></a></p>
<style>@media (min-width: 769px) { main .article-section .content ul:not([class]):not([id]) li ul:not([class]):not([id]) { margin-top: 0; margin-bottom: 24px; } } main .article-section .content ul:not([class]):not([id]) li, main .article-section .content ul:not([class]):not([id]) > li {padding-bottom: 18px;} main .article-section .content ul:not([class]):not([id]) li ul:not([class]):not([id]) li {padding-bottom: 0;} img.alignico {margin-right: 10px;margin-top: 5px;float: left;} summary {display: list-item;cursor: pointer;font-style: italic; } section.article-section a {color: #7755f3} code {color: red;} #roman-pronskiy,.copy-heading:has(#roman-pronskiy){margin-top: 0;} main li a[href^="https://github.com"]:before {background: no-repeat 2px center url(data:image/svg+xml;utf8;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNCIgaGVpZ2h0PSIxNCIgZmlsbD0ibm9uZSIgdmlld0JveD0iMCAwIDMyIDMyIj48cGF0aCBmaWxsPSIjMjQyOTJFIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNiAwYTE2IDE2IDAgMCAwLTUgMzEuMmMuNy4xIDEtLjQgMS0uOHYtM2MtNCAuOC01LTEtNS40LTEuOC0uMS0uNS0xLTItMS42LTIuMy0uNi0uMy0xLjQtMSAwLTEgMS4yIDAgMi4xIDEuMSAyLjQgMS42IDEuNSAyLjQgMy44IDEuNyA0LjcgMS4zLjEtMSAuNi0xLjcgMS0yLjEtMy41LS40LTcuMy0xLjgtNy4zLTggMC0xLjcuNy0zLjEgMS43LTQuMi0uMi0uNC0uNy0yIC4xLTQuMyAwIDAgMS40LS40IDQuNCAxLjdhMTQuOCAxNC44IDAgMCAxIDggMGMzLjEtMi4xIDQuNC0xLjcgNC40LTEuNyAxIDIuMi40IDMuOS4yIDQuM2E2IDYgMCAwIDEgMS42IDQuM2MwIDYuMS0zLjcgNy41LTcuMyA3LjkuNi41IDEuMSAxLjQgMS4xIDN2NC4zYzAgLjQuMyAxIDEuMS44QTE2IDE2IDAgMCAwIDE2IDBaIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48L3N2Zz4=);content: "";padding-left: 20px;} main li a[href^="https://www.youtube.com"]:before {background: no-repeat 0px center url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='100%25' version='1.1' viewBox='0 0 68 48' width='100%25'%3E%3Cpath class='ytp-large-play-button-bg' d='m .66,37.62 c 0,0 .66,4.70 2.70,6.77 2.58,2.71 5.98,2.63 7.49,2.91 5.43,.52 23.10,.68 23.12,.68 .00,-1.3e-5 14.29,-0.02 23.81,-0.71 1.32,-0.15 4.22,-0.17 6.81,-2.89 2.03,-2.07 2.70,-6.77 2.70,-6.77 0,0 .67,-5.52 .67,-11.04 l 0,-5.17 c 0,-5.52 -0.67,-11.04 -0.67,-11.04 0,0 -0.66,-4.70 -2.70,-6.77 C 62.03,.86 59.13,.84 57.80,.69 48.28,0 34.00,0 34.00,0 33.97,0 19.69,0 10.18,.69 8.85,.84 5.95,.86 3.36,3.58 1.32,5.65 .66,10.35 .66,10.35 c 0,0 -0.55,4.50 -0.66,9.45 l 0,8.36 c .10,4.94 .66,9.45 .66,9.45 z' fill='%23FF0000' fill-opacity='0.81'%3E%3C/path%3E%3Cpath d='m 26.96,13.67 18.37,9.62 -18.37,9.55 -0.00,-19.17 z' fill='%23fff'%3E%3C/path%3E%3Cpath d='M 45.02,23.46 45.32,23.28 26.96,13.67 43.32,24.34 45.02,23.46 z' fill='%23ccc'%3E%3C/path%3E%3C/svg%3E"); content: "";padding-left: 18px;background-size: 16px;}</style>
<p>Welcome to the May edition of PHP Annotated!</p>
<p>It’s been a minute since the last edition. Turns out time flies when you’re deep in foundation work, and the occasional existential debugging session. But we are back.</p>
<p>Here’s everything you might’ve missed in the PHP world, we dug through the noise so you can just enjoy the highlights.</p>
<h2 id="highlights">Highlights</h2>
<ul>
<li>
<h3><a href="https://blog.jetbrains.com/phpstorm/2025/05/join-jetbrains-phpverse-to-celebrate-30-years-of-php/">Join PHPverse to Celebrate 30 Years of PHP!</a></h3>
<p>PHP is turning 30 this year! 🎉 PHPverse is a free online event hosted by JetBrains to mark this amazing milestone together.</p>
<p><strong><a href="https://lp.jetbrains.com/phpverse-2025/#registration-for-jetbrains-phpverse" target="_blank" rel="noopener">Register</a></strong><br />
📅 <strong>June 17, 2025</strong><br />
🕚 <strong>11:30–16:30 UTC</strong></p>
<p>And if you’re feeling nostalgic, check out my experiment from last year, where I actually <em>run</em> PHP 1.0:</p>
<p> <iframe width="560" height="315" src="https://www.youtube.com/embed/0BPExYh5Anw?si=ejRL8fMzJ3gLnRZU" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</li>
<li>
<h3><a href="https://www.php.net/ChangeLog-8.php#8.1.32" target="_blank" rel="noopener">PHP 8.1.32</a>, <a href="https://www.php.net/ChangeLog-8.php#8.2.28" target="_blank" rel="noopener">PHP 8.2.28</a>, <a href="https://www.php.net/ChangeLog-8.php#8.3.19" target="_blank" rel="noopener">PHP 8.3.19</a>, and <a href="https://www.php.net/ChangeLog-8.php#8.4.5" target="_blank" rel="noopener">PHP 8.4.5</a> Released</h3>
<p>❗️These security updates address several vulnerabilities, including:<br />
<a href="https://github.com/php/php-src/security/advisories/GHSA-p3x9-6h7p-cgfc" target="_blank" rel="noopener">CVE-2025-1219</a>, <a href="https://github.com/php/php-src/security/advisories/GHSA-hgf5-96fm-v528" target="_blank" rel="noopener">CVE-2025-1736</a>, <a href="https://github.com/php/php-src/security/advisories/GHSA-52jp-hrpf-2jff" target="_blank" rel="noopener">CVE-2025-1861</a>, <a href="https://github.com/php/php-src/security/advisories/GHSA-pcmh-g36c-qc44" target="_blank" rel="noopener">CVE-2025-1734</a>, <a href="https://github.com/php/php-src/security/advisories/GHSA-v8xr-gpvj-cx9g" target="_blank" rel="noopener">CVE-2025-1217</a>, and <a href="https://github.com/php/php-src/security/advisories/GHSA-wg4p-4hqh-c3g9" target="_blank" rel="noopener">out-of-bounds read when using <code>XML_OPTION_SKIP_TAGSTART</code></a>.</p>
</li>
<li>
<h3><a href="https://thephp.foundation/blog/2025/04/10/php-core-security-audit-results/" target="_blank" rel="noopener">PHP Core Security Audit Results</a></h3>
<p>In 2024, the first in a decade external security audit of the PHP core was conducted by <a href="https://thephp.foundation/" target="_blank" rel="noopener">The PHP Foundation</a>, and commissioned by the <a href="https://www.sovereign.tech/" target="_blank" rel="noopener">Sovereign Tech Agency</a>. The audit focused on the most critical parts of the PHP source code, given the limited budget.</p>
<p>The audit uncovered 27 issues, 17 of which had security implications. All identified issues have since been fixed by the PHP development team.</p>
<p>PHP users are strongly encouraged to upgrade to the latest PHP versions to benefit from these security improvements.</p>
</li>
<li>
<h3><a href="https://thephp.foundation/blog/2025/05/15/frankenphp/" target="_blank" rel="noopener">FrankenPHP Is Now Officially Supported by The PHP Foundation</a></h3>
<p><a href="https://frankenphp.dev/" target="_blank" rel="noopener">FrankenPHP</a>, a modern PHP application server built for performance and ease of deployment, is now officially supported by The PHP Foundation.</p>
<p>It’s built in Go, powered by the Caddy web server, and offers a fresh take on running PHP outside of the traditional FPM model.</p>
</li>
<li>
<h3 id="run-php-inside-node">Run PHP Inside Node.js with <a href="https://github.com/platformatic/php-node" target="_blank" rel="noopener">@platformatic/php-node</a></h3>
<p>php-node is a Rust-based, Node.js-native module that allows running PHP applications within a Node.js environment. It works by dispatching requests to a PHP instance running multi-threaded in the worker pool provided by the Node.js. This means you can enjoy the performance of Node.js while utilizing PHP’s extensive ecosystem and functionality.</p>
<p>Here is a hello world example:<br />
<img decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2025/05/Screenshot-2025-05-28-at-10.45.57-PM.png" alt="php-node">
</p>
<p>👉 <a href="https://blog.platformatic.dev/seamlessly-blend-php-with-nodejs" target="_blank" rel="noopener">Intro blog post</a></p>
</li>
<li>
<h3><a href="https://liamhammett.com/think-of-an-elephpant" target="_blank" rel="noopener">Think of an Elephpant</a> 🐘</h3>
<blockquote style="color: white; background-color: orangered;"><p>PHP doesn’t need defending. It needs celebrating.</p></blockquote>
<p>A brilliant message and post from Liam Hammett!</p>
</li>
</ul>
<h2 id="php-core">PHP Core</h2>
<ul>
<li>
<h3>✅ <a href="https://wiki.php.net/rfc/pipe-operator-v3" target="_blank" rel="noopener">RFC: Pipe operator v3</a></h3>
<p>The <a href="https://wiki.php.net/rfc/pipe-operator" target="_blank" rel="noopener">first proposal</a> to bring a pipe operator to PHP dates back to 2016, followed by a <a href="https://wiki.php.net/rfc/pipe-operator-v2" target="_blank" rel="noopener">second attempt</a> in 2020. Now, on the third try it’s finally happening!</p>
<p>Starting with PHP 8.5, you’ll be able to use the <code>|></code> operator to chain function calls in a clean, readable way:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false" data-enlighter-title="">
$result = "Hello World"
|> htmlentities(...)
|> str_split(...)
|> fn($x) => array_map(strtoupper(...), $x)
|> fn($x) => array_filter($x, fn($v) => $v != 'O');
</pre>
<p>Kudos to Larry Garfield for the persistence and effort to see this through!</p>
<p>Check out Brent’s video <a href="https://www.youtube.com/watch?v=UG_yb_WOutE" target="_blank" rel="noopener">overview of PHP 8.5’s pipe operator</a>.</p>
</li>
<li>
<h3>✅ <a href="https://wiki.php.net/rfc/array_first_last" target="_blank" rel="noopener">RFC: array_first() and array_last()</a></h3>
<p>PHP 7.3 introduced <code>array_key_first()</code> and <code>array_key_last()</code> to get the first and last keys of an array, but still no functions to get the first and last values of an array.</p>
<p>Thanks to Niels Dossche two new functions will be added in PHP 8.5:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false" data-enlighter-title="">
array_first([1 => 'a', 0 => 'b', 3 => 'c', 2 => 'd']); // 'a'
array_last([1 => 'a', 0 => 'b', 3 => 'c', 2 => 'd']); // 'd'
</pre>
</li>
<li>
<h3>✅ <a href="https://wiki.php.net/rfc/marking_return_value_as_important" target="_blank" rel="noopener">RFC: Marking return values as important (#[\NoDiscard])</a></h3>
<p>PHP 8.5 will come with a new <code>#[\NoDiscard]</code> attribute to indicate that the return value of a function or method is “important” and that not doing anything with the return value is likely to be a bug.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false" data-enlighter-title="">
#[\NoDiscard("this is important")]
function foo(): int {
return 0;
}
$results = foo(); // No warning, because the return value is consumed by the assignment
foo();// Warning: The return value of function foo() is expected to be consumed
</pre>
</li>
<li>
<h3>📣 <a href="https://wiki.php.net/rfc/true_async" target="_blank" rel="noopener">RFC: True Async</a></h3>
<p>Edmond Dantes proposes to create a standard for writing concurrent code in PHP, as well as a C-API interface that will allow PHP to be extended at a low level with C, Rust, C++, and other languages. This will allow modules to support non-blocking I/O operations without having to override PHP functions or duplicate code.</p>
<p>As Edmond clarified on the mailing list, the plan is to simplify the current RFC as much as possible to make it easier to pass. New syntax should be discussed closer to future versions of PHP (likely 9.0).</p>
<p>You can follow the development process almost live in this separate <a href="https://github.com/true-async/" target="_blank" rel="noopener">github.com/true-async</a>.</p>
</li>
<li>
<h3><a href="https://wiki.php.net/todo/php85" target="_blank" rel="noopener">PHP 8.5 release managers announced</a></h3>
<p>As is tradition, PHP 8.5 will have 2 rookie release managers: <a href="https://github.com/edorian" target="_blank" rel="noopener">Volker Dusch</a> and <a href="https://github.com/DanielEScherzer" target="_blank" rel="noopener">Daniel Scherzer</a>. They will be assisted by veteran RM <a href="https://github.com/adoy" target="_blank" rel="noopener">Pierrick Charron</a>.</p>
</li>
<p>There are a few other RFCs with mentioning that passed, are under discussion, or were declined already:</p>
<ul>
<li><strong>✅ <a href="https://wiki.php.net/rfc/get-error-exception-handler" target="_blank" rel="noopener">RFC: Add get_error_handler(), get_exception_handler() functions</a></strong></li>
<li><strong>📣 <a href="https://wiki.php.net/rfc/final_promotion" target="_blank" rel="noopener">RFC: Final Property Promotion</a></strong></li>
<li><strong>📣 <a href="https://wiki.php.net/rfc/modern_compression" target="_blank" rel="noopener">RFC: Modern Compression</a></strong></li>
<li><strong>📣 <a href="https://wiki.php.net/rfc/exception_ignore_args_default_value" target="_blank" rel="noopener">RFC: Change default zend.exception_ignore_args INI setting</a></strong></li>
<li><strong>❌ <a href="https://wiki.php.net/rfc/short-and-inner-classes" target="_blank" rel="noopener">RFC: Nested Classes</a></strong></li>
<li><strong>❌ <a href="https://wiki.php.net/rfc/never-parameters-v2" target="_blank" rel="noopener">RFC: Never Parameters (v2)</a></strong></li>
</ul>
</ul>
<h2 id="tools">Tools</h2>
<ul>
<li><a href="https://github.com/PHP-WebRTC" target="_blank" rel="noopener"><strong>PHP WebRTC</strong></a>
<p>A full implementation of the WebRTC protocol in pure PHP! No Node.js or JavaScript is required on the backend to use. You would need FFI enabled, however.</p>
<p>The goal is to make it easy to build WebRTC-based apps in pure PHP – including media servers, video conference web app, SFUs, and peer-to-peer apps.</p>
<p>Bravo! 👏</p>
</li>
<li><a href="https://github.com/phpacker/phpacker" target="_blank" rel="noopener"><strong>phpacker/phpacker</strong></a>
<p>Package any PHP script or PHAR into a standalone, cross-platform executable.</p>
<p>I briefly explained how this works in my blogpost <a href="https://pronskiy.com/blog/php-script-as-binary/" target="_blank" rel="noopener">Turn Any PHP Script into a Native Single-File Binary</a>. Phacker makes it really easy and builds for Mac, Linux, and Windows!</p>
</li>
<li><a href="https://github.com/NativePHP" target="_blank" rel="noopener"><strong>NativePHP 1.0</strong></a>
<p>The tool allows building desktop application with PHP and JavaScript/HTML/CSS.</p>
<p>There is also a paid version for iOS and Android devices: <a href="https://nativephp.com/mobile" target="_blank" rel="noopener">NativePHP for Mobile</a>.</p>
</li>
<li><a href="https://github.com/boson-php/boson" target="_blank" rel="noopener"><strong>boson-php/boson</strong></a>
<p>This is another tool to build cross-platform desktop apps with PHP, JavaScript, HTML, and CSS. But instead of Electron, under the hood, it uses <a href="https://github.com/saucer/saucer" target="_blank" rel="noopener">saucer/saucer</a>, a modern, cross-platform C++ webview library, which allows making apps really slim:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false" data-enlighter-title="">$app = new Boson\Application();
$app->webview->html = <<<'HTML'
<button onclick="foo('HELLO');">Hello</button>
HTML;
$app->webview->bind('foo', var_dump(...));
$app->run();</pre>
</li>
<li><strong>NPX-like package execution tools for PHP</strong>
<p>There are a few such tools available out there:</p>
<ul>
<li>
<p><a href="https://github.com/imliam/cpx" target="_blank" rel="noopener">imliam/cpx</a> – Run any command from any composer package, even if it’s not installed in your project.</p>
</li>
<li>
<p><a href="https://github.com/eduardocruz/phpx" target="_blank" rel="noopener">eduardocruz/phpx</a> – PHPX is a NPX-like package execution tool for PHP.</p>
</li>
</ul>
<p>I also <span style="text-decoration: line-through;">vibe-coded</span> prototyped my own tool in Python. The difference is that it does not require users to have PHP or Composer on their machine: <a href="https://github.com/pronskiy/pocus" target="_blank" rel="noopener">pronskiy/pocus</a>.</p>
<p>Originally I wanted my friends who have <code>uvx</code> installed to be able to use my MCP servers with no additional prerequisites. So it allows them to run any script from GitHub with one command:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false" data-enlighter-title="">
uvx pocus https://github.com/pronskiy/mcp examples/echo.php</pre>
<p>Or any command from an existing PHP package:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false" data-enlighter-title="">uvx pocus phpstan/phpstan phpstan analyse ./src</pre>
</li>
<li><a href="https://github.com/masan4444/phpup" target="_blank" rel="noopener"><strong>masan4444/phpup</strong></a> – Cross-Platform PHP version manager written in Rust. Seems abandoned, but maybe someone wants to pick it up?
</li>
<li><a href="https://github.com/php-internal/dload" target="_blank" rel="noopener"><strong>php-internal/dload</strong></a> – Helps to download binaries from release assets.
</li>
<li><a href="https://github.com/utopia-php/vcs" target="_blank" rel="noopener"><strong>utopia-php/vcs</strong></a> – Lite & fast micro PHP vcs abstraction library that is easy to use.
</li>
<li><a href="https://github.com/jerowork/graphql-attribute-schema" target="_blank" rel="noopener"><strong>jerowork/graphql-attribute-schema</strong></a> – Easily build your GraphQL schema using PHP attributes instead of large configuration arrays.
</li>
<li><a href="https://github.com/mario-deluna/php-glfw" target="_blank" rel="noopener"><strong>mario-deluna/php-glfw</strong></a><strong> – A fully-featured OpenGL and GLFW extension for PHP.</strong>
<p>At first I thought it was another fun project to make cool demos like these:</p>
<p><a href="https://github.com/medilies/php-pong" target="_blank" rel="noopener">medilies/php-pong</a> – Classic Pong game programmed with pure OpenGL wrapped in OOP code.<br />
<img decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2025/05/pingpong.gif" alt="ping-pong" width="300"></p>
<p><a href="https://github.com/mario-deluna/php-chip8" target="_blank" rel="noopener">mario-deluna/php-chip8</a> – Yet another CHIP-8 emulator, but in PHP!.<br />
<img decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2025/05/chhip8-php.gif" alt="" width="300"></p>
<p>Or even like this:<br />
<a href="https://github.com/phpgl/php-craft" target="_blank" rel="noopener">phpgl/php-craft</a> – Mining PHPotentials: A Minecraft-Inspired Game written in PHP.<br />
<img decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2025/05/php-craft.jpg" alt="" width="300"></p>
<p>But it actually has useful applications, for example: <a href="https://station.clancats.com/creating-animated-code-snippets-with-tempest-highlighting-and-php-glfw/" target="_blank" rel="noopener"><strong>creating animated code snippets with Tempest Highlight & PHP-GLFW</strong></a>:</p>
<p> <img decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2025/05/output_shorter.gif" alt="" width="416">
</li>
</ul>
<h2 id="ai">AI</h2>
<p>The ecosystem of AI tooling for PHP is growing fast! Here are just a few interesting findings.</p>
<h3>MCP</h3>
<p>If you’re new to the topic, check out my short intro video: <a href="https://www.youtube.com/watch?v=qPVtVulhFC4" target="_blank" rel="noopener"><strong>MCP – What is that?</strong></a></p>
<p>I found the interface of <a href="https://github.com/logiscapedev/mcp-sdk-php" target="_blank" rel="noopener">logiscapedev/mcp-sdk-php</a> a bit frustrating to work with, so I built a simple wrapper to make things easier:</p>
<ul>
<li>
<p><a href="https://github.com/pronskiy/mcp" target="_blank" rel="noopener"><strong>pronskiy/mcp</strong></a> – 🐉 The fast, PHP way to build MCP servers.</p>
<p>Here’s what a minimal MCP server looks like using the wrapper:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false" data-enlighter-title="">
new \Pronskiy\Mcp\Server(name: 'echo-server')
->tool(
name: 'echo' ,
description 'Echoes text',
fn (string $text) => $text
)->run();
</pre>
</li>
</ul>
<p>But there is a much more powerful alternative:</p>
<ul>
<li><a href="https://github.com/php-mcp/server" target="_blank" rel="noopener"><strong>php-mcp/server</strong></a> – PHP implementation for the Model Context Protocol (MCP) server.<br />
<img decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2025/05/mcp-server.jpeg" alt="mcp server" width="500">
</li>
</ul>
<h3>LLM Frameworks</h3>
<ul>
<li><a href="https://github.com/LLPhant/LLPhant" target="_blank" rel="noopener"><strong>LLPhant/LLPhant</strong></a> – A lightweight and intuitive framework with tons of ready-to-use tools. Well-documented, actively developed.</li>
<li><a href="https://github.com/inspector-apm/neuron-ai" target="_blank" rel="noopener"><strong>inspector-apm/neuron-ai</strong></a> – Open source framework to create full featured AI Agents in PHP.</li>
</ul>
<h3>Using AI models in PHP</h3>
<p>Thanks to an open <a href="https://onnxruntime.ai/" target="_blank" rel="noopener">ONNX</a> format, it is possible to run many AI models in PHP natively. These are the tools that facilitate it:</p>
<ul>
<li><a href="https://github.com/CodeWithKyrian/transformers-php" target="_blank" rel="noopener"><strong>CodeWithKyrian/transformers-php</strong></a></li>
<li><a href="https://github.com/ankane/onnxruntime-php" target="_blank" rel="noopener"><strong>ankane/onnxruntime-php</strong></a></li>
</ul>
<h2 id="phpstorm">PhpStorm</h2>
<ul>
<li>
<p><a href="https://www.jetbrains.com/phpstorm/whatsnew/" target="_blank" rel="noopener"><strong>PhpStorm 2025.1</strong></a><br />
This release is a major update that includes improvements in PHPStan annotations, Xdebug, and WordPress support. It also includes new subscription system for JetBrains AI with AI Free tier.</p>
<p>And speaking of AI, <a href="https://www.jetbrains.com/junie/" target="_blank" rel="noopener"><strong>Junie, the coding agent by JetBrains</strong></a>, is now available for PhpStorm too!</p>
<p> <iframe width="560" height="315" src="https://www.youtube.com/embed/tfBS85Ksfag?si=uARUJg0qxvn-Sedx" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</li>
<li>
<p><a href="https://blog.jetbrains.com/phpstorm/2025/03/jetbrains-xdebug-helper/"><strong>New JetBrains Xdebug Helper browser extension</strong></a>.</p>
</li>
<li>
<p><a href="https://github.com/xepozz/crontab-intellij-plugin" target="_blank" rel="noopener"><strong>Cron & Crontab Support IDE Plugin</strong></a> – Crontab syntax support: schedule periods and errors highlighting, shell command completions, human-readable schedule folding, and more.</p>
</li>
<li>
<p><a href="https://www.jetbrains.com/phpstorm/laravel/" target="_blank" rel="noopener"><strong>Laravel Idea 10 released</strong></a> – The popular plugin for PhpStorm has received a major update. It includes the Database tables and fields completion, code generation improvements, and more.</p>
</li>
</ul>
<h2 id="frameworks">Frameworks</h2>
<ul>
<li><a href="https://laravel-news.com/laravel-12" target="_blank" rel="noopener"><strong>Laravel 12 is Now Released</strong></a>
<p>This update introduces new starter kits for React, Vue, and Livewire</p>
</li>
<li><a href="https://github.com/capevace/laravel-introspect" target="_blank" rel="noopener">Capevace/laravel-introspect</a> – Query your Laravel codebase like a database, with an Eloquent-like API.
</li>
<li><a href="https://github.com/fdomgjoni99/laravel-ai-factory" target="_blank" rel="noopener">fdomgjoni99/laravel-ai-factory</a> – A package for generating realistic test data using AI models.
</li>
<li><a href="https://tighten.com/insights/10-efficient-and-fun-ways-to-seed-your-database/" target="_blank" rel="noopener">10 Efficient (and Fun) Ways to Seed Your Database</a> by Nico Devs.
</li>
<li><a href="https://christalks.dev/post/secure-your-webhooks-in-laravel-preventing-data-spoofing-fe25a70e" target="_blank" rel="noopener">Secure Your Webhooks in Laravel: Preventing Data Spoofing</a> by Chris.
</li>
<li><a href="https://tomasvotruba.com/blog/custom-phpstan-rules-to-improve-every-symfony-project" target="_blank" rel="noopener">Custom PHPStan Rules to Improve Every Symfony project</a> by Tomas Votruba.
</li>
<li><a href="https://github.com/symfony/symfony/pull/60578" target="_blank" rel="noopener"><strong>Symfony 7.3.0 has been released</strong></a>
<p>Check the <a href="https://symfony.com/blog/category/living-on-the-edge/7.3" target="_blank" rel="noopener">Living on the Edge</a> category on this blog to learn about the main features of this new stable release.</p>
</li>
</ul>
<h2 id="misc">Misc</h2>
<ul>
<li><a href="https://dailyrefactor.com/php-3-to-8-the-evolution-of-a-codebase" target="_blank" rel="noopener">PHP 3 to 8: The Evolution of a Codebase</a> by Aleksander Kaim.
</li>
<li><a href="https://tomasvotruba.com/blog/create-weird-fun-phpstan-rules" target="_blank" rel="noopener">Create Weird Fun PHPStan Rules like Nobody’s Watching</a> by Tomas Votruba.
</li>
<li><a href="https://ides.dev/notes/discovering-php-yield-keyword/" target="_blank" rel="noopener">Discovering PHP’s yield Keyword After 10 Years</a> by Will.
</li>
<li><a href="https://derickrethans.nl/and-then-there-was-pie.html" target="_blank" rel="noopener">And Then There Was PIE</a> by Derick Rethans.
</li>
<li><a href="https://tideways.com/profiler/blog/combining-regular-expressions-with-named-capture-groups-to-improve-performance" target="_blank" rel="noopener">Combining regular expressions with named capture groups to improve performance</a> by Benjamin Eberlei.
</li>
</ul>
<h2 id="conferences">Conferences</h2>
<p>These PHP events are all worth a visit, and some are still accepting presentation proposals:</p>
<ul>
<li><a href="https://lp.jetbrains.com/phpverse-2025/" target="_blank" rel="noopener">PHPverse 2025</a> – June 17, Online.</li>
<li><a href="https://2025.kphpug.jp/" target="_blank" rel="noopener">PHP Conference Kansai 2025</a> – July 18–19, Kobe, Japan.</li>
<li><a href="https://laracon.us/" target="_blank" rel="noopener">Laracon 2025</a> – July 29–30, Denver, CO, USA.</li>
<li><a href="https://api-platform.com/fr/con/2025/" target="_blank" rel="noopener">API Platform Conference 2025</a> – September 18–19, Lille, France.</li>
<li><a href="https://event.afup.org/" target="_blank" rel="noopener">Forum PHP 2025</a> – October 9–10, Disneyland Paris, France.</li>
<li><a href="https://phpconference.com/munich/" target="_blank" rel="noopener">International PHP Conference 2025</a> – October 27‒31, Munich, Germany.</li>
<li><a href="https://live.symfony.com/2025-amsterdam-con/" target="_blank" rel="noopener">SymfonyCon Amsterdam 2025</a> – November 27-28. Amsterdam, Netherlands. <a href="https://live.symfony.com/2025-amsterdam-con/cfp" target="_blank" rel="noopener">CFP</a></li>
</ul>
<p>To find a PHP meetup happening near you, check out <a href="https://www.php.net/cal.php?cm=05&cy=2024" target="_blank" rel="noopener">the calendar on php.net</a>.</p>
<h2 id="fun">Fun</h2>
<ul>
<li><a href="https://justfuckingusehtml.com/" target="_blank" rel="noopener">Just fucking use HTML</a></li>
<li><a href="https://x.com/teamsplog/status/1923845486005354617" target="_blank"><br />
<img decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2025/05/Screenshot-2025-05-28-at-12.36.46-PM.png" alt="" width="483"><br />
</a></li>
<li><a href="https://x.com/pronskiy/status/1925116718801719598" target="_blank"><br />
<img decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2025/05/php-is-sexy.jpeg" alt="PHP is sexy" width="300"><br />
</a></li>
</ul>
<hr/>
<p>If you have any interesting or useful links to share via PHP Annotated, please leave a comment on this post or let us know on X/Twitter: <a href="https://twitter.com/pronskiy" target="_blank" rel="noopener">@pronskiy</a>.</p>
<p><a href="https://info.jetbrains.com/PHP-Annotated-Subscription.html" target="_blank" rel="noopener">Subscribe to PHP Annotated</a></p>
<hr />
</p>
<p>If you have any interesting or useful links to share via PHP Annotated, please leave a comment on this post or let us know <a href="https://twitter.com/pronskiy" target="_blank" rel="noopener">on X/Twitter</a>.</p>
<p style="text-align: left;" align="center"><a style="color: white" class="jb-download-button" title="Complete this form and get PHP Annotated Monthly delivered fresh to your email" href="https://info.jetbrains.com/PHP-Annotated-Subscription.html" target="_blank" rel="noopener">Subscribe to PHP Annotated</a></p>
<div class="about-author ">
<div class="about-author__box">
<div class="row">
<div class="about-author__box-img">
<img decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2022/07/php-annotated-roman.png" alt="" loading="lazy">
</div>
<div class="about-author__box-text">
<h4>Roman Pronskiy</h4>
<p>Developer Advocate at <a href="https://twitter.com/PhpStorm" target="_blank" rel="noopener">@PhpStorm</a>, Executive Director at <a href="https://twitter.com/ThePHPF" target="_blank" rel="noopener">@The PHP Foundation</a>.</p>
<p><a href="https://twitter.com/pronskiy" target="_blank" rel="noopener">Twitter</a> | <a href="https://github.com/pronskiy" target="_blank" rel="noopener">GitHub</a></p>
</div>
</div>
</div>
</div>
Moving a Perforce P4 P4ROOT to a different drive - Rob Allenhttps://akrabat.com/?p=73492025-05-27T10:00:00.000Z<p>On one of my servers here, I run a local <a href="https://www.perforce.com/products/helix-core">Perforce P4</a> server for my son. He's a game developer and as they use P4 at work, he wanted to learn it in a sandbox and to have somewhere familiar to put his own work.</p>
<p>Installation onto Ubuntu was easy enough and I provided access outside of our local network via <a href="https://tailscale.com">Tailscale</a> and all was well.</p>
<p>Recently, I was doing some admin-stuff on the server and realised that the drive that P4 was using was the local 256GB drive rather than one of the big 8TB drives in the box, so I needed to move it. The directory that the instance was using was <tt>/opt/perforce/servers/p4d-holland1</tt>. This needed moving to <tt>/media/ssd1/data/perforce/servers/p4d-holland1</tt>.</p>
<p><strong>Note</strong> I run a single instance, very default, not-very-important P4 server. Don't just copy these steps blindly if your setup is more complicated or mission critical! Make sure that you <a href="https://help.perforce.com/helix-core/server-apps/p4sag/2024.1/Content/P4SAG/moving-same-machine.html">read the docs</a>.</p>
<p>These are the steps I followed:</p>
<ul>
<li>Stop the instance: <tt>p4dctl stop p4d-holland1</tt></li>
<li>Move the data by copying it and renaming the old directory (just in case):
<pre>
mkdir -p /media/ssd1/data/perforce/servers/p4d-holland1
cp -r /opt/perforce/servers/p4d-holland1/* /media/ssd1/data/perforce/servers/p4d-holland1/
mv /opt/perforce/servers/p4d-holland1 /opt/perforce/servers/p4d-holland1-old
</pre>
</li>
<li>Edit <tt>/etc/perforce/p4dctl.conf.d/p4d-holland1.conf</tt> and update the <tt>P4ROOT</tt> to the new directory</li>
<li>Start the instance again: <tt>p4dctl start p4d-holland1</tt></li>
</ul>
<p>I didn't see any errors, so I got my son to check it. After he said it was all good, I removed the old <tt>/opt/perforce/servers/p4d-holland1-old</tt> directory.</p>
<p>My son now has more than enough space to continue his side-projects.</p>
Updated rst2pdf website - Rob Allenhttps://akrabat.com/?p=73552025-05-20T10:00:00.000Z<p>Earlier this year, <a href="https://lornajane.net/about">Lorna</a> spent some time updating <a href="https://rst2pdf.org">rst2pdf</a>'s website to use <a href="https://www.sphinx-doc.org/en/master/">Sphinx</a>. The nice thing about Sphinx is that it uses <a href="https://docutils.sourceforge.io/rst.html">restructuredText</a>, the same as rst2pdf does, so we now stay in the same ecosystem.</p>
<p>While, we could have continued using Jekyll, it makes much more sense for us to use the same markup language as we use for the main tool, and our manual is written in it too. With the Jekyll system, we used docutils with a generic and simple CSS file to create the HTML version of the manual, but now as Sphinx is rST native, it builds the HTML version of the manual and it as it is now styled the same as the website, it looks much better. Of course, we use rst2pdf to generate the PDF version ;)</p>
<p>Rather handily, we now search on the site and also have a dark mode which you can see in this screenshot if your OS is in dark mode as you view this page. I also like that you can click on the "eye" icon and view the rST source for the page.</p>
<picture><source
srcset="https://akrabat.com/wp-content/uploads/2025/05/2025-05-rst2pdf.org-dark.png"
media="(prefers-color-scheme: dark)"
/><source
srcset="https://akrabat.com/wp-content/uploads/2025/05/2025-05-rst2pdf.org-light.png"
media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)"
/><br />
<img decoding="async" src="https://akrabat.com/wp-content/uploads/2025/05/2025-05-rst2pdf.org-light.png" loading="lazy" alt="2025 05 rst2pdf.org light." width="600"/>
</picture>
<p>This required a fair amount of work. Thanks Lorna for taking the time to do it!</p>
PhpStorm 2025.2 Early Access Program Is Now Open - PhpStorm : The IDE that empowers PHP developers | The JetBrains Bloghttps://blog.jetbrains.com/?post_type=phpstorm&p=5675662025-05-19T12:37:06.000Z
<p>We’re opening the Early Access Program (EAP) for the next major PhpStorm 2025.2 release.</p>
<figure class="wp-block-image size-full"><img decoding="async" fetchpriority="high" width="2560" height="1440" src="https://blog.jetbrains.com/wp-content/uploads/2025/05/PS-social-BlogFeatured-2560x1440-1-1.png" alt="" class="wp-image-567599"/></figure>
<div class="buttons">
<div class="buttons__row">
<a href="https://www.jetbrains.com/phpstorm/nextversion/" class="btn" target="_blank" rel="noopener">Download PhpStorm 2025.2 EAP</a>
</div>
</div>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading">About Early Access Program (EAP)</h2>
<p>If you’re not familiar with how our Early Access Program (EAP) works, here’s a quick overview:</p>
<ol>
<li>We release new EAP builds weekly, giving you a sneak peek at upcoming features.</li>
<li>EAP builds are completely free to use and do not require a license.</li>
<li>You can install the EAP version alongside your stable PhpStorm installation, so there’s no need to uninstall your current version.</li>
<li>The most convenient way to access EAP builds and keep both your stable and EAP versions up-to-date is by using <a href="https://www.jetbrains.com/toolbox-app/" target="_blank" rel="noopener">our Toolbox App</a>.</li>
<li>Alternatively, you can download EAP builds from the <a href="https://www.jetbrains.com/phpstorm/nextversion/" target="_blank" rel="noopener">EAP page</a> or set up your IDE to automatically receive updates by selecting <em>Check IDE Updates for the Early Access Program</em> under <em>Settings/Preferences | Appearance & Behavior | System Settings | Updates</em>.</li>
</ol>
1-2-3 PHP operators - Exakathttps://www.exakat.io/?p=157982025-05-17T09:59:04.000Z<figure id="attachment_15799" aria-describedby="caption-attachment-15799" style="width: 300px" class="wp-caption alignleft"><a href="https://www.exakat.io/wp-content/uploads/2025/05/123.320.jpg"><img decoding="async" class="wp-image-15799 size-medium" src="https://www.exakat.io/wp-content/uploads/2025/05/123.320-300x300.jpg" alt="1-2-3 operators in PHP" width="300" height="300" srcset="https://www.exakat.io/wp-content/uploads/2025/05/123.320-150x150@2x.jpg 300w, https://www.exakat.io/wp-content/uploads/2025/05/123.320-150x150.jpg 150w, https://www.exakat.io/wp-content/uploads/2025/05/123.320-100x100.jpg 100w, https://www.exakat.io/wp-content/uploads/2025/05/123.320.jpg 320w" sizes="(max-width: 300px) 100vw, 300px" /></a><figcaption id="caption-attachment-15799" class="wp-caption-text">1-2-3 operators in PHP</figcaption></figure>
<h1 id="toc_0">1-2-3 PHP operators</h1>
<p>I learnt my PHP on a European keyboard, and it was always a pain to use some strange symbols. When I moved to Canada, I quickly realized that the chosen symbols were actually very easily accessible on the keyboard: it was just my layout that was less practical.</p>
<p>Then, I also realized how these symbols are repeatedly used to make larger ones. There is the single <code>=</code> for assignation, then the double <code>==</code> for equality and then the triple <code>===</code> for identity. It is probably easier to repeat an existing symbol, than to summon a new one, which might be cumbersome to reach on the keyboard.</p>
<p>There is a bit of laziness in this pattern. <code>==</code> might easily mistaken with <code>===</code> (and vice versa) and the mistake might be begnin (or not), but mistaking <code>=</code>and <code>==</code> is, most of the time, a bug.</p>
<p>Here is as selection of 1-2-3 PHP operators, along with their documentation.</p>
<table>
<thead>
<tr>
<th>1</th>
<th>2</th>
<th>3</th>
</tr>
</thead>
<tbody>
<tr>
<td>.</td>
<td>..</td>
<td>…</td>
</tr>
<tr>
<td>+</td>
<td>++</td>
<td>+++</td>
</tr>
<tr>
<td>–</td>
<td>—</td>
<td>—</td>
</tr>
<tr>
<td>*</td>
<td>**</td>
<td>***</td>
</tr>
<tr>
<td>/</td>
<td>//</td>
<td>///</td>
</tr>
<tr>
<td>></td>
<td>>></td>
<td>>>></td>
</tr>
<tr>
<td><</td>
<td><<</td>
<td><<<</td>
</tr>
<tr>
<td>=</td>
<td>==</td>
<td>===</td>
</tr>
<tr>
<td>@</td>
<td>@@</td>
<td>@@@</td>
</tr>
<tr>
<td>?</td>
<td>??</td>
<td>???</td>
</tr>
<tr>
<td>:</td>
<td>::</td>
<td>:::</td>
</tr>
<tr>
<td>!</td>
<td>!!</td>
<td>!!!</td>
</tr>
<tr>
<td>$</td>
<td>$$</td>
<td>$$$</td>
</tr>
</tbody>
</table>
<h2 id="toc_1">Dots in PHP</h2>
<ul>
<li><code>.</code> is the operator of <a href="https://www.php.net/manual/en/language.operators.string.php">string concatenation</a>.</li>
<li><code>..</code> is a syntax error. It is not supported in PHP. In other language, it denotes a range between two operators.</li>
<li><code>...</code> is the <a href="https://www.php.net/manual/en/language.types.array.php">ellipsis operator</a>, to spread or collect values out or in an array</li>
</ul>
<h2 id="toc_2">Plus in PHP</h2>
<ul>
<li><code>+</code> is the <a href="https://www.php.net/manual/en/language.operators.arithmetic.php">addition operator</a>, for numbers and arrays.</li>
<li><code>++</code> is the <a href="https://www.php.net/manual/en/language.operators.increment.php">increment operator</a>. It comes in two flavors, pre-increment or post increment.</li>
<li><code>+++</code> is a syntax oddity, where there is no space between an incremented variable and its addition with another value: <code>$b = $a+++ 1;</code> is <code>$b = $a++ + 1;</code></li>
</ul>
<h2 id="toc_3">Minus in PHP</h2>
<ul>
<li><code>-</code> is the <a href="https://www.php.net/manual/en/language.operators.arithmetic.php">substraction operator</a>, for numbers only.</li>
<li><code>--</code> is the <a href="https://www.php.net/manual/en/language.operators.increment.php">decrement operator</a>. It comes in two flavors, pre-increment or post increment.</li>
<li><code>---</code> is a syntax oddity, where there is no space between a decremented variable and its substraction with another value: <code>$b = $a--- 1;</code> is <code>$b = $a-- - 1;</code></li>
</ul>
<h2 id="toc_4">Star in PHP</h2>
<ul>
<li><code>*</code> is the <a href="https://www.php.net/manual/en/language.operators.arithmetic.php">multiplication operator</a>, for numbers.</li>
<li><code>**</code> is the <a href="https://www.php.net/manual/en/language.operators.arithmetic.php">power operator</a>. It is the multiplication of multiplications, basically.</li>
<li><code>***</code> is not supported. Math has a notion of towers of powers, where <code>4 *** 4</code> would be <code>4 ** (4 ** 4)</code>. That number is already beyond the integer range, so the operator is probably unuseful.</li>
</ul>
<h2 id="toc_5">Slash in PHP</h2>
<ul>
<li><code>/</code> is the <a href="https://www.php.net/manual/en/language.operators.arithmetic.php">decimal division operator</a>, for numbers.</li>
<li><code>//</code> is a <a href="https://www.php.net/manual/en/language.basic-syntax.comments.php">one line comment</a>.</li>
<li><code>///</code> is a one line comment, starting with a slash.</li>
</ul>
<h2 id="toc_6">Greater than in PHP</h2>
<ul>
<li><code>></code> is the <a href="https://www.php.net/manual/en/language.operators.comparison.php">greater than comparison operator</a>.</li>
<li><code>>></code> is the <a href="https://www.php.net/manual/en/language.operators.bitwise.php">right bitshift operator</a>, which moves bits to the right.</li>
<li><code>>>></code> is not supported.</li>
</ul>
<h2 id="toc_7">Lesser than in PHP</h2>
<ul>
<li><code><</code> is the <a href="https://www.php.net/manual/en/language.operators.comparison.php">lesser than comparison operator</a>.</li>
<li><code><<</code> is the <a href="https://www.php.net/manual/en/language.operators.bitwise.php">left bitshift operator</a>, which moves bits to the left.</li>
<li><code><<<</code> is the start of the Heredoc and Nowdoc sequence.</li>
</ul>
<h2 id="toc_8">Equal in PHP</h2>
<ul>
<li><code>=</code> is the assignation operator, which gives a value to a variable.</li>
<li><code>==</code> is the <a href="https://www.php.net/manual/en/language.operators.comparison.php">comparison operator</a>. Types are adapted before the comparison actually happens.</li>
<li><code>===</code> is the <a href="https://www.php.net/manual/en/language.operators.comparison.php">identity operator</a>, which compares values and their types.</li>
</ul>
<h2 id="toc_9">Question in PHP</h2>
<ul>
<li><code>?</code> is half the <a href="https://www.php.net/manual/en/language.operators.comparison.php#language.operators.comparison.ternary">ternary operator</a>, along with the colon.</li>
<li><code>??</code> is the <a href="https://www.php.net/manual/en/language.operators.comparison.php#language.operators.comparison.coalesce">null coalesce</a> operator. It is a ternary operator, specialized with null values.</li>
<li><code>???</code> is a <a href="https://github.com/search?q=%3F%3F%3F+language%3APHP+&type=code">classic comment</a> in the code, when someone was surprised by some code.</li>
</ul>
<h2 id="toc_10">Colon @ in PHP</h2>
<ul>
<li><code>:</code> is the label operator, <code>label:</code>, that works with <a href="https://www.php.net/manual/en/control-structures.goto.php">goto</a>. It is also the other half of the <a href="https://www.php.net/manual/en/language.operators.comparison.php#language.operators.comparison.ternary">ternary operator</a>.</li>
<li><code>::</code> is the <a href="https://www.php.net/manual/en/language.oop5.paamayim-nekudotayim.php">scope resolution operator</a>, which reachs data at the class level, and not at the object level.</li>
<li><code>:::</code> is not supported.</li>
</ul>
<h2 id="toc_11">Exclamation ! in PHP</h2>
<ul>
<li><code>!</code> is the <a href="https://www.php.net/manual/en/language.operators.logical.php">negation operator</a>: it turns any value into its boolean opposite.</li>
<li><code>!!</code> is the <a href="https://www.php.net/manual/en/language.operators.logical.php">negation negation operator</a>. It is basically a shorter <code>(boolean)</code> cast operator, and a barbarism coming from other languages.</li>
<li><code>!!!</code> is the <a href="https://www.php.net/manual/en/language.operators.logical.php">negation operator</a>, but three times longer to write.</li>
</ul>
<h2 id="toc_12">Arobase @ in PHP</h2>
<ul>
<li><code>@</code> is the <a href="https://www.php.net/manual/en/language.operators.errorcontrol.php">noscream operator</a>, which mutes error reporting on a syntax.</li>
<li><code>@@</code> is the <a href="https://www.php.net/manual/en/language.operators.errorcontrol.php">noscream operator</a>, which mutes error reporting on a error-muted expression. It is in case the code should really not emit errors.</li>
<li><code>@@@</code> is the <a href="https://www.php.net/manual/en/language.operators.errorcontrol.php">noscream operator</a>, which mutes error reporting on a doubly error-muted expression. It is in case the code should really really, and I mean really, not emit errors.</li>
</ul>
<h1 id="toc_13">1-2-3 operators are fun!</h1>
<p>It is interesting to discover the different meanings of an symbol as it is repeated. It might vary a lot in meaning and usage.</p>
<p>It is also interesting to realise that some of these repeated symbol operators are not used yet, and might be promoted to an actual PHP features. Replacing <code>range(1, 19)</code> by <code>1..19</code> would be a nice new feature for PHP 8.5.</p>
<p>We could also dream of <code>:::</code>, <code>|||</code>, <code>___</code> or even quadruples operators <code>....</code> or <code>@@@@</code>. Well, sometimes, we already have them!</p>
<p>The post <a href="https://www.exakat.io/1-2-3-php-operators/">1-2-3 PHP operators</a> appeared first on <a href="https://www.exakat.io">Exakat</a>.</p>
Join JetBrains PHPverse to Celebrate 30 Years of PHP - PhpStorm : The IDE that empowers PHP developers | The JetBrains Bloghttps://blog.jetbrains.com/?post_type=phpstorm&p=5640002025-05-16T14:33:48.000Z
<p>PHP turns 30 this June, and we’re looking forward to celebrating this notable milestone together with you! </p>
<p>On June 17, 2025, we’re hosting <a href="https://lp.jetbrains.com/phpverse-2025/" target="_blank" rel="noopener">JetBrains PHPverse 2025</a>, where prominent people from the PHP community will come together and share their thoughts about the state and the future of the modern PHP language and ecosystem. This is a free online event with inspiring talks, discussions, Q&A sessions, and an exclusive PHP anniversary merch giveaway. </p>
<div class="buttons">
<div class="buttons__row">
<a href="https://lp.jetbrains.com/phpverse-2025/" class="btn" target="" rel="noopener">Grab my spot</a>
</div>
</div>
<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe title="JetBrains PHPverse 2025 – Join us to celebrate PHP’s 30th birthday!" width="500" height="281" src="https://www.youtube.com/embed/J3WamGSE2No?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>
<h2 class="wp-block-heading">Agenda</h2>
<p><strong>When:</strong> 11:30–17:00 UTC on June 17, 2025.</p>
<p><strong>Where:</strong> Online</p>
<p><strong>Hosted by:</strong> Brent Roose, JetBrains Developer Advocate for PhpStorm and creator of the <a href="https://www.youtube.com/@phpannotated" target="_blank" rel="noopener">PHP Annotated</a> YouTube channel, and <a href="https://x.com/naderman" target="_blank">Nils Adermann</a>, co-founder of <a href="https://packagist.com/" target="_blank" rel="noopener">Packagist</a> and co-creator of <a href="https://getcomposer.org/" target="_blank" rel="noopener">Composer</a>.</p>
<p><strong>Language:</strong> English</p>
<p><strong>Lineup:</strong> </p>
<ul>
<li><em>FrankenPHP: Reinventing PHP for the Modern Web</em> by <a href="https://x.com/dunglas" target="_blank">Kévin Dunglas</a></li>
<li><em>Symfony: Current State and Future Plans</em> by <a href="https://x.com/nicolasgrekas" target="_blank">Nicolas Grekas</a></li>
<li><em>Building MCP Servers With PHP</em> by <a href="https://x.com/marcelpociot" target="_blank">Marcel Pociot</a></li>
<li><em>A PhpStorm Announcement You Won’t Want to Miss!</em></li>
<li><em>Laravel Q&A With Its Creator</em> by <a href="https://x.com/taylorotwell" target="_blank">Taylor Otwell</a></li>
<li><em>PHP Anniversary Merch</em> <em>giveaway</em></li>
<li><em>The Future of PHP Education</em> by <a href="https://x.com/jeffrey_way" target="_blank">Jeffrey Way</a>, <a href="https://twitter.com/PovilasKorop" target="_blank" rel="noopener">Povilas Korop</a>, and <a href="https://x.com/zenstruck" target="_blank">Kevin Bond</a></li>
<li><em>How AI Is Changing the Tech Industry</em> by <a href="https://linktr.ee/cheukting" target="_blank" rel="noopener">Cheuck Ting Ho</a></li>
<li><em>PHP Foundation: Growing PHP for the Future</em> by <a href="https://x.com/pronskiy" target="_blank">Roman Pronsky</a> and <a href="https://gpb.moe/" target="_blank" rel="noopener">Gina Banyard</a></li>
</ul>
<p>For more information about the speakers and their talks, see the <a href="https://lp.jetbrains.com/phpverse-2025/#agenda" target="_blank" rel="noopener">PHPverse 2025</a> page.</p>
<h2 class="wp-block-heading">What to expect</h2>
<p>Each talk will be 20 minutes long with a 10-minute Q&A session afterward, so you are welcome to ask the speakers any questions you may have. Besides talks and the Q&A, we’ve prepared one panel discussion and a couple of exciting announcements!</p>
<p>We’ll stream the event on the official <a href="https://www.youtube.com/@JetBrainsTV" target="_blank" rel="noopener">JetBrains YouTube channel</a>, and all of the talks will remain available after the event is over so you can catch up on any you miss. </p>
<div class="blockquote">
<blockquote><p>“I’m super excited to celebrate 30 years of PHP together with the community and hope to see you there!”</p></blockquote>
<div class="blockquote__author">
<img decoding="async" class="blockquote__author-img" src="https://blog.jetbrains.com/wp-content/uploads/2024/09/brent-roose.jpg" alt="Brent Roose, JetBrains Developer Advocate">
<div class="blockquote__author-info">
<strong class="blockquote__author-title">Brent Roose, <a href="https://www.youtube.com/@phpannotated" target="_blank" rel="noopener">PHP Annotated</a> YouTube channel</strong>
<span class="blockquote__author-subtitle">Developer Advocate for PhpStorm</span>
</div>
</div>
</div>
<p>Can’t make it on June 17? <a href="https://lp.jetbrains.com/phpverse-2025/#registration-for-jetbrains-phpverse" target="_blank" rel="noopener">Register</a> anyway and you’ll receive the link with the talk recordings via email after the event.</p>
<h2 class="wp-block-heading">Ask speakers now</h2>
<p>Already have questions for our speakers? Leave them in the comments below this blog post, and the event hosts will make sure to ask them during the Q&A sessions. </p>
<p>Get ready and help spread the word around the PHP community!</p>
<figure class="wp-block-image size-full"><a href="https://lp.jetbrains.com/phpverse-2025/" target="_blank" rel="noopener"><img decoding="async" fetchpriority="high" width="1300" height="650" src="https://blog.jetbrains.com/wp-content/uploads/2025/04/phpverse-email-banner.png" alt="" class="wp-image-564019"/></a></figure>
Using OneOf for a property in an OpenAPI spec - Rob Allenhttps://akrabat.com/?p=73302025-05-13T10:00:00.000Z<p>When writing an <a href="https://www.openapis.org">OpenAPI specification</a>, I came across the need for a particular property in an error response to be either a string or an object.</p>
<p>This situation came about when validating a POST request that takes an <tt>items</tt> property that is a list of objects</p>
<p>As a contrived example of a pet with accessories, consider this example request in <tt>curl</tt>:</p>
<pre>
curl -X "POST" "http://api.example.com/pets" \
-H 'Content-Type: application/json' \
-d $'{
"name": "Rover",
"items": [
{
"name": "collar"
"count": 1
},
{
"name": "bowl"
"count": 2
}
],
}'
</pre>
<p>For the <tt>items</tt> property, there are two error responses if validation fails:</p>
<ol>
<li>Property missing or wrong type.<br />For example:
<pre>
{
"items": "Items property is required"
}
</pre>
</li>
<li>Missing or invalid sub property<br />For example on the <tt>count</tt> property of the second item:
<pre>
{
"items": [
{
},
{
"count": "Item count must be a positive integer"
}
]
}
</pre>
</li>
</ol>
<p>To document this in OpenAPI, I wrote the following for my <tt>'400'</tt> response (simplified):</p>
<pre>
'400':
description: Validation failed
content:
application/json:
schema:
type: object
properties:
errors:
type: object
properties:
name:
type: string
example: "Pet name must not be empty"
items:
oneOf:
- $ref: '#/components/schemas/ValidationErrorPetItemsObject'
- $ref: '#/components/schemas/ValidationErrorPetItemsString'
</pre>
<p>Within the <tt>components</tt> -> <tt>schemas</tt> section, we define both schemas:</p>
<pre>
ValidationErrorPetItemsString:
type: string
example: "Items property is required"
ValidationErrorPetItemsPropertyObject:
type: array
items:
type: object
properties:
id:
type: string
example: "Item name is required"
count:
type: string
example: "Item count must be a positive integer"
</pre>
<p>I don't know how to specify that an empty array will be included so that the client can work out which item object has the problem, so for now it is documented in the description.</p>
<p>There's probably other ways to solve this, but this is the one I came up with. If there's a better way, let me know.</p>
Additional parameters on a PHP interface method - Rob Allenhttps://akrabat.com/?p=73332025-05-06T10:00:00.000Z<p>On the <a href="http://discord.gg/roave">Roave Discord</a> recently, there was a discussion about not breaking BC in interfaces inspired by <a href="https://phpc.social/@gromnan/114347134096322334">this post by Jérôme Tamarelle</a>:</p>
<p><img fetchpriority="high" decoding="async" src="https://akrabat.com/wp-content/uploads/2025/04/2025gromnan-post.jpeg" alt="Gromnan post." title="gromnan-post.jpeg" border="0" width="500" height="379" /></p>
<p>It's clearly true that if you add a new parameter to a method on an interface, then that's a BC break as every concrete implementation of the interface needs to change their signature.</p>
<p>However, Gina commented that you don't need to use <tt>func_get_arg()</tt> as concrete implementations can <em>add additional optional arguments</em>.</p>
<p><strong>WHAT?!!!</strong></p>
<p>I didn't know this and it had never occurred to me to try, so I had to go <a href="https://3v4l.org/#live">3v4l.org</a> and check</p>
<pre><?php
interface Foo
{
public function bar(int $a): void;
}
class Baz implements Foo
{
public function bar(int $a, int $b=2): void
{
echo "$a: $b";
}
}
(new Baz)->bar(1,3);
</pre>
<p>Sure enough, this code <a href="https://3v4l.org/judRV">works</a>!</p>
<p>I don't have much of a need for this, but it's useful to know.</p>
Some thoughts on LLM usage in my work - Rob Allenhttps://akrabat.com/?p=73392025-04-29T10:00:00.000Z<p>While it would be nice to put the genie back in the bottle, that hasn't happened often in human history, so for the foreseeable future, AI in the form of LLMs are here to stay. I imagine that what we use them for will change over time as we collectively internalise their limitations. </p>
<p>Personally, I'm now using them for my work as much as I reasonably can. This is involving many different areas, such as asking questions that I would previously have just googled. While the LLM does make things up, the first 40 answers on Google can be decidedly less than helpful nowadays too. The LLM will read many more webpage results than I can be bothered to, provide a summary and cite its sources so that I can click a few to get a feel for how much I trust it.</p>
<p>When developing, I'm using the LLM for multiple tasks. It can help me find bugs when I paste in snippets of code and say "this code is getting X wrong. Tell me why and propose a fix". I generally find that this level of focussed question works quite well, though sometimes, it will go off on a tangent and never come back.</p>
<p>In an IDE, I've found LLM-based smart-autocomplete from something like Copilot works quite well. Again, it's a focussed small task and so tends to work reasonably well for me. I've also found that writing a comment as I would normally do helps the LLM get it right.</p>
<p>New to me is Claude Code. This command line tool reads the files in the current directory and can operate on them. I've found that it is able to do bigger chunks of work as a result and I use <tt>git diff</tt> to assess its work and tweak as necessary before I personally commit. </p>
<p>One thing that is obvious in hindsight, but if I don't have a clear idea in my mind of what I need, then the tools will do worse. This can be more clearly seen with the larger blocks of work, so now, if I'm exploring how and what I want to do, I will talk with the LLM more conversationally more like a brainstorming session to get to the point where I know what I want to do. Then I can instruct it to do the work. This works no differently from trying to delegate a job to a junior and sounds so obvious when written down. However, in the excitement of seeing the magic of the LLM doing the right thing, it's easy to forget and then be surprised when it goes so so wrong.</p>
<p>Not every tool is useful for everyone or useful in every situation. There are also ethical and environmental considerations that affect how people view any given technology and I do not want to suggest that LLM tools must be used; they don't. </p>
<p>I realise that I'm not using these tools are much as others, however, I'm finding them useful.</p>
Quine in PHP : self displaying code - Exakathttps://www.exakat.io/?p=157832025-04-24T20:21:39.000Z<h2 id="toc_0">Quines for PHP<a href="https://www.exakat.io/wp-content/uploads/2025/04/twins.320.jpg"><img fetchpriority="high" decoding="async" class="alignleft wp-image-15784 size-medium" src="https://www.exakat.io/wp-content/uploads/2025/04/twins.320-300x300.jpg" alt="self displaying code" width="300" height="300" srcset="https://www.exakat.io/wp-content/uploads/2025/04/twins.320-150x150@2x.jpg 300w, https://www.exakat.io/wp-content/uploads/2025/04/twins.320-150x150.jpg 150w, https://www.exakat.io/wp-content/uploads/2025/04/twins.320-100x100.jpg 100w, https://www.exakat.io/wp-content/uploads/2025/04/twins.320.jpg 320w" sizes="(max-width: 300px) 100vw, 300px" /></a></h2>
<p>A <a href="https://en.wikipedia.org/wiki/Quine_(computing)">quine</a> is a piece of code that executes to produce itself. This is a self displaying code. The execution of the code outputs the same source code, before it was executed. No changes happened, and one may run it again, to the same result.</p>
<p>While this code curiosity has rare direct applications, the challenge leads to interesting cases and manipulations. Here are a few of them.</p>
<h2 id="toc_1">Quine with print()</h2>
<div>
<pre class="brush: php; title: ; notranslate">
<?php
$code = '<?php
$code = %s;
printf($code, 39, $code, 39);
';
printf($code, 39, $code, 39);
</pre>
</div>
<p>The <code>$code</code> variable contains the source code as a string, with <code>%s</code> as a placeholder for where <code>$code</code>will be inserted.</p>
<p>The <code>printf()</code> function uses <code>%s</code> to format the string, and the <code>39</code> arguments represent the ASCII value for single quotes <code>'</code>. So, <code>printf($code, 39, $code, 39)</code> effectively wraps $code in quotes for the output. When executed, this script outputs its own source code exactly as written, fulfilling the quine requirement. This is a straightforward and compact quine, relying on string formatting to replicate itself.</p>
<h2 id="toc_2">A Quine Using var_export</h2>
<p>Another pragmatic approach is to use <code>var_export()</code>, which outputs a parseable string representation of a variable. Here’s an example:</p>
<div>
<pre class="brush: php; title: ; notranslate">
<?php
$a = '<?php $a = %s; echo sprintf($a, var_export($a, true));';
echo sprintf($a, var_export($a, true));
</pre>
</div>
<p>The <code>$a</code> variable holds the source code template with a %s placeholder. <code>var_export($a, true)</code>generates a string representation of <code>$a</code>, including quotes, which is then inserted into the template using <code>sprintf()</code>. When executed, <code>echo</code> outputs the entire script, matching the source code. This method is similar to the first but uses <code>var_export()</code> to handle the string representation of the code, which can be particularly useful for ensuring the output is a valid PHP string.</p>
<h2 id="toc_3">A Quine With a Function and get<em>defined</em>vars()</h2>
<p>Here’s a more creative approach using a function and PHP’s get<em>defined</em>vars to access the source code:<code></code></p>
<pre class="brush: php; title: ; notranslate"> <?php function q() { $v = get_defined_vars(); echo "<?php\nfunction q() {\n \$v = get_defined_vars();\n echo \$v['s'];\n}\n\$s = " . var_export($v['s'], true) . ";\nq();\n"; } $s = "<?php\nfunction q() {\n \$v = get_defined_vars();\n echo \$v['s'];\n}\n\$s = " . var_export($s, true) . ";\nq();\n"; q(); </pre>
<p><code></code></p>
<p>The function <code>q()</code> uses <code>get_defined_vars()</code> to access all variables in its scope, including <code>$s</code>, which contains the source code. The <code>$s</code> variable is a string that includes the entire script, with <code>var_export()</code>used to properly format <code>$s</code> within itself. When <code>q()</code>is called, it outputs the value of <code>$s</code>, which is the source code itself. This quine is more complex because it uses a function and PHP’s introspection capabilities, but it still achieves the same goal.</p>
<h2 id="toc_4">A Quine Using File Reading</h2>
<p>Some quines cheat by reading their own source file, though this is often disallowed in strict quine competitions because it uses external input (the file itself). Still, it’s a common technique in PHP:<code></code></p>
<pre class="brush: php; title: ; notranslate"> <?php echo file_get_contents(__FILE__); </pre>
<p><code></code></p>
<p><strong>FILE</strong> is a PHP magic constant that gives the full path to the current script.<code>file_get_contents(__FILE__)</code> reads the script’s source code and echo outputs it. This outputs the exact source code, but it’s considered a “cheating quine” because it relies on reading the file rather than generating the code internally.</p>
<h2 id="toc_5">Quine with PHP native functions</h2>
<p>This is a quine by Tim Bond. It is based on reading the source of the quine in a file, but without <code>file_get_contents()</code>, and with a twist.</p>
<div>
<pre class="brush: php; title: ; notranslate">
<?php
echo html_entity_decode(strip_tags(highlight_file($_SERVER['SCRIPT_FILENAME'], true)));
</pre>
</div>
<p>The file is read with <code>highlight_file()</code>, but cleaned into text with <code>strip_tags()</code> and <code>html_entity_decode()</code>. This is quite a detour to read the source, but it works flawlessly.</p>
<h2 id="toc_6">Quine and chr()</h2>
<p>A nice and rather unreadable quine from <a href="https://gist.github.com/zuchmanski/953463">Zuchmanski</a>.</p>
<div>
<pre class="brush: php; title: ; notranslate">
<?
$a='chr(60).chr(63).chr(10).chr(36).chr(97).chr(61).chr(39).$a.chr(39).chr(59).chr(10)."echo $a;".chr(10).chr(63).chr(62)';
echo chr(60).chr(63).chr(10).chr(36).chr(97).chr(61).chr(39).$a.chr(39).chr(59).chr(10)."echo $a;".chr(10).chr(63).chr(62);
?>
</pre>
</div>
<p>This quine is based on the ASCII representation of the code, which is turned into actual characters with the <code>chr()</code> function, and then glued together with the concatenation. With a bit of ASCII knowledge, you will find the characters to write <code>a</code> (aka, 97) or <code>'</code> (39, as seen previously). The code is injected in a variable, then echo-ed to the output again later.</p>
<h2 id="toc_7">Quine and chr()</h2>
<p>Here is the quine that waited for years to be published, by <a href="https://phpc.social/@b_viguier">Benoit Viguier</a>.</p>
<div>
<pre class="brush: php; title: ; notranslate">
<?=(fn($s)=>"<?=$s(\x27$s\x27)?>")('(fn($s)=>"<?=$s(\x27$s\x27)?>")')?>
</pre>
</div>
<p>It is based on arrow functions: look among the pile of punctuations, the arrow function (<code>fn($s)</code>) is defined, then immediately called upon itself, with the code in a string. The quotes are hidden with a character sequence <code>\x27</code>, and there is not a single semi colon in sight. Quite a feat for the PHP parser!</p>
<h2 id="toc_8">PHP Quine</h2>
<p>There are many ways to create a quine in PHP. <code>echo</code> or <code>print</code> for the display, and a duplication of the code in a variable which is later displayed. The result is quite fascinating: it is a fixed point, and it is guaranteed to exists, thanks to PHP being a language Turing complete!</p>
<p>Now, where will we use these in an actual application?</p>
<p>The post <a href="https://www.exakat.io/quine-in-php-self-displaying-code/">Quine in PHP : self displaying code</a> appeared first on <a href="https://www.exakat.io">Exakat</a>.</p>