<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>PHP | Nerdpress.org</title>
	<atom:link href="https://nerdpress.org/category/php/feed/" rel="self" type="application/rss+xml" />
	<link>https://nerdpress.org</link>
	<description>...dev, tech problems and solutions.</description>
	<lastBuildDate>Thu, 16 Apr 2026 14:19:33 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>
	<item>
		<title>PHP&#8217;s serialize_precision hidden danger</title>
		<link>https://nerdpress.org/2026/04/16/phps-serialize_precision-hidden-danger/</link>
		
		<dc:creator><![CDATA[Ivo Bathke]]></dc:creator>
		<pubDate>Thu, 16 Apr 2026 14:19:02 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Debugging]]></category>
		<category><![CDATA[JSON]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=3477</guid>

					<description><![CDATA[<p>The PHP configuration directive serialize_precision can cause hard to debug issues. When deviated from its default value, it can lead to inexpected behavior in common functions like json_encode() and serialize(). The Problem If you change the serialize_precision setting from its default value of -1, the float precision will be more digits than 2 decimal places.If &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2026/04/16/phps-serialize_precision-hidden-danger/" class="more-link">Continue reading<span class="screen-reader-text"> "PHP&#8217;s serialize_precision hidden danger"</span></a></p>
The post <a href="https://nerdpress.org/2026/04/16/phps-serialize_precision-hidden-danger/">PHP’s serialize_precision hidden danger</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p>The PHP configuration directive <code>serialize_precision</code> can cause hard to debug issues.</p>



<p>When deviated from its default value, it can lead to inexpected behavior in common functions like <code>json_encode()</code> and <code>serialize()</code>.</p>



<span id="more-3477"></span>



<h2 class="wp-block-heading"><strong>The Problem</strong></h2>



<p>If you change the <code>serialize_precision</code> setting from its default value of <strong>-1</strong>, the float precision will be more digits than 2 decimal places.<br />If you are not aware of this setting, it can lead to hard-to-debug issues in your application.</p>



<p>This behavior can silently break payload validation, API responses, or any logic that relies on exact string comparisons of numeric values or which assumes that float values come with 2 decimal places.</p>



<h2 class="wp-block-heading"><strong>Impact on Functions</strong></h2>



<p>When <code>serialize_precision</code> is set to something other than <strong>-1</strong>, the following functions are affected:</p>



<ul class="wp-block-list">
<li>json_encode()</li>



<li>json_decode()</li>



<li>serialize()</li>



<li>unserialize()</li>
</ul>



<p>Consider the following example.<br />Setting a high precision changes the number in the JSON output:</p>


<pre class="wp-block-code"><span><code class="hljs language-xml"><span class="php"><span class="hljs-meta">&lt;?php</span>
<span class="hljs-comment">// High precision</span>
ini_set(<span class="hljs-string">"serialize_precision"</span>, <span class="hljs-number">100</span>);
<span class="hljs-keyword">echo</span> json_encode(<span class="hljs-number">12.12</span>);
<span class="hljs-comment">// Output: 12.1199999999999992184029906638897955417633056640625</span>

<span class="hljs-comment">// Default (recommended) precision</span>
ini_set(<span class="hljs-string">"serialize_precision"</span>, <span class="hljs-number">-1</span>);
<span class="hljs-keyword">echo</span> json_encode(<span class="hljs-number">12.12</span>);
<span class="hljs-comment">// Output: 12.12</span></span></code></span></pre>


<h2 class="wp-block-heading"><strong>How to Check Your Configuration</strong></h2>



<p>So better verify your current setting, especially when dealing with mysterious floating-point issues in legacy environments.<br />When on shared hosting, you might even not know that the setting was changed.</p>



<p>You can check it via code:</p>


<pre class="wp-block-code"><span><code class="hljs language-php"><span class="hljs-keyword">echo</span> ini_get(<span class="hljs-string">"serialize_precision"</span>);</code></span></pre>


<p>Or, if you are using PHP 8.5 or newer, you can conveniently check for configuration differences using the CLI:</p>


<pre class="wp-block-code"><span><code class="hljs">php --ini=diff</code></span></pre>


<p>This will highlight any settings that differ from the PHP defaults, making it much easier to spot such dangerous configurations.</p>



<h2 class="wp-block-heading"><strong>Conclusion</strong></h2>



<p>If you encounter unexpected floating-point behavior in your PHP application, check the <code>serialize_precision</code> setting first. It&#8217;s a simple fix that can save you hours of debugging.</p>



<p></p>The post <a href="https://nerdpress.org/2026/04/16/phps-serialize_precision-hidden-danger/">PHP’s serialize_precision hidden danger</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Symfony HTTP Client default timeout</title>
		<link>https://nerdpress.org/2025/08/06/symfony-http-client-default-timeout/</link>
		
		<dc:creator><![CDATA[Ivo Bathke]]></dc:creator>
		<pubDate>Wed, 06 Aug 2025 07:02:56 +0000</pubDate>
				<category><![CDATA[Symfony]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=3428</guid>

					<description><![CDATA[<p>Small Service Post: The default timeout of the Symfony HTTP Client is 60s (seconds)! Sure, it is mentioned in the Symfony HTTP Client docs, that PHP&#8217;s default_socket_timeout is taken as default value.So what is the default_socket_timeout then, you might wonder? One click away in PHP Runtime Configuration docs you will find that in the php.ini &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2025/08/06/symfony-http-client-default-timeout/" class="more-link">Continue reading<span class="screen-reader-text"> "Symfony HTTP Client default timeout"</span></a></p>
The post <a href="https://nerdpress.org/2025/08/06/symfony-http-client-default-timeout/">Symfony HTTP Client default timeout</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p><br />Small Service Post:<br /><br />The default timeout of the Symfony HTTP Client is <strong>60s</strong> (seconds)!<br /><br />Sure, it is mentioned in the <a href="https://symfony.com/doc/current/reference/configuration/framework.html#timeout" title="">Symfony HTTP Client docs</a>, that PHP&#8217;s <em>default_socket_timeout</em> is taken as default value.<br />So what is the default_socket_timeout then, you might wonder?</p>



<p>One click away in <a href="https://www.php.net/manual/en/filesystem.configuration.php" target="_blank" rel="noopener" title="">PHP Runtime Configuration docs</a> you will find that in the php.ini the default value is &#8220;60&#8221;. <br />In case you wonder what unit these &#8220;60&#8221; is, you will find <em>(in seconds)</em> a bit down below the config table. <br />&#8211; Yeah, seconds was my first guess, but I need to assure it is not microseconds by any chance. &#8211;</p>



<p>And thats it! Thats the post.</p>



<p>P.S: It is funny how scattered information sometimes is, although is all there. <br />AI did know, but did not name a source. <br />And since AI is not to trust, I still had to look it up via Search.</p>The post <a href="https://nerdpress.org/2025/08/06/symfony-http-client-default-timeout/">Symfony HTTP Client default timeout</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Patch dependencies with composer-patches</title>
		<link>https://nerdpress.org/2025/04/11/patch-dependencies-with-composer-patches/</link>
		
		<dc:creator><![CDATA[Ivo Bathke]]></dc:creator>
		<pubDate>Fri, 11 Apr 2025 10:10:56 +0000</pubDate>
				<category><![CDATA[Composer]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[composer]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=3406</guid>

					<description><![CDATA[<p>Sometimes, you may encounter a bug or an unwanted functionality in a PHP vendor dependency, and forking the package and maintaining upstream changes can be too cumbersome. In such cases, using composer-patches is a good solution. Composer-patches is a handy Composer plugin that applies diff patches to specific packages during installation. Basically, you store a &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2025/04/11/patch-dependencies-with-composer-patches/" class="more-link">Continue reading<span class="screen-reader-text"> "Patch dependencies with composer-patches"</span></a></p>
The post <a href="https://nerdpress.org/2025/04/11/patch-dependencies-with-composer-patches/">Patch dependencies with composer-patches</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p>Sometimes, you may encounter a bug or an unwanted functionality in a PHP vendor dependency, and forking the package and maintaining upstream changes can be too cumbersome. In such cases, using <strong><a href="https://github.com/cweagans/composer-patches" target="_blank" rel="noopener" title="">composer-patches</a></strong> is a good solution.</p>



<p><strong>Composer-patches</strong> is a handy Composer plugin that applies diff patches to specific packages during installation.</p>



<p>Basically, you store a diff patch in your project, specify which vendor package it should be applied to in your <code>composer.json</code>, and the plugin will apply the patch to the original code of the vendor package after it got installed by composer.</p>



<span id="more-3406"></span>



<p>The <a href="https://docs.cweagans.net/composer-patches/" target="_blank" rel="noopener" title="">documentation</a> provides detailed instructions on how to set up the plugin.<br />In short, you need:</p>



<ul class="wp-block-list">
<li>Install the plugin (we&#8217;re using the v2 beta) with:<br /><code>composer require cweagans/composer-patches:^2.0.0-beta2</code></li>



<li>Create a patch. The plugin relies on Git as the patcher, so you can use Git to generate the patch file. (See below)<br />However, I recommend using another package to create the patch file:<br /><a class="" href="https://github.com/symplify/vendor-patches">https://github.com/symplify/vendor-patches</a><br />This package provides an opinionated but reasonable way to create and store your patches. <strong>vendor-patches</strong> takes care of the correct paths in the patch file, which might not be immediately obvious: Pathes should be relative to the vendor package root and not relative to your projects root dir.</li>



<li>Add the patch instructions to your <code>composer.json</code> by including a <code>patches</code> section under <code>extra</code>. For example:</li>
</ul>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-string">"extra"</span>: {
  <span class="hljs-string">"patches"</span>: {
    <span class="hljs-string">"oxid-esales/oxideshop-ce"</span>: {
      <span class="hljs-string">"Disable user registration due to spam attacks"</span>: <span class="hljs-string">"./patches/disable-registerUser.patch"</span>
    }
  }
}</code></span></pre>


<p>On your next <code>composer install</code> the vendor package will be patched and the vendor&#8217;s <br />code will be altered according the patch.</p>



<p>Some notes: If you encounter the error:</p>



<pre class="wp-block-preformatted">Could not apply patch! Skipping. The error was: Cannot apply patch disable-registerUser.patch</pre>



<p>You might have incorrect paths in your patch file. As mentioned above, the paths need to be relative to the vendor root directory, not your project&#8217;s root directory.<br />So to manually create the diff patches, run this command:</p>



<pre class="wp-block-preformatted">diff --git a/source/Application/Component/UserComponent.php b/source/Application/Component/UserComponent.php</pre>



<p><br />This is <strong>wrong</strong> usage from project root dir:</p>



<pre class="wp-block-preformatted">diff --git a/vendor/oxid-esales/oxideshop-ce/source/Application/Component/UserComponent.php b/vendor/oxid-esales/oxideshop-ce/source/Application/Component/UserComponent.php</pre>



<p>Happy patching! :)</p>The post <a href="https://nerdpress.org/2025/04/11/patch-dependencies-with-composer-patches/">Patch dependencies with composer-patches</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Doctrine: WHERE IN with array of integers</title>
		<link>https://nerdpress.org/2025/01/17/doctrine-where-in-with-array-of-integers/</link>
		
		<dc:creator><![CDATA[Ivo Bathke]]></dc:creator>
		<pubDate>Fri, 17 Jan 2025 16:34:32 +0000</pubDate>
				<category><![CDATA[Doctrine ORM]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Doctrine]]></category>
		<category><![CDATA[Sql]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=3381</guid>

					<description><![CDATA[<p>Since I stumbled across this the other day, here&#8217;s how you build an SQL query with a WHERE IN condition for an array of integers. This is surprisingly not intuitive, as you cannot simply pass an array of integers to a prepared statement. Instead, you need to add special binding types to inform Doctrine that &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2025/01/17/doctrine-where-in-with-array-of-integers/" class="more-link">Continue reading<span class="screen-reader-text"> "Doctrine: WHERE IN with array of integers"</span></a></p>
The post <a href="https://nerdpress.org/2025/01/17/doctrine-where-in-with-array-of-integers/">Doctrine: WHERE IN with array of integers</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p>Since I stumbled across this the other day, here&#8217;s how you build an SQL query with a <code>WHERE IN</code> condition for an array of integers.</p>



<p>This is surprisingly not intuitive, as you cannot simply pass an array of integers to a prepared statement. Instead, you need to add special binding types to inform Doctrine that this is indeed an array of integers.</p>



<p>It&#8217;s very well explained in the documentation under the Parameters Conversion section:<br /><a href="https://www.doctrine-project.org/projects/doctrine-dbal/en/current/reference/data-retrieval-and-manipulation.html#list-of-parameters-conversion">https://www.doctrine-project.org/projects/doctrine-dbal/en/current/reference/data-retrieval-and-manipulation.html#list-of-parameters-conversion</a><br />But it is rather hard to find and it took me a while.</p>



<p>The trick is to add the array binding types to the the types parameters to the query method. In the case of integers, it is<code> \Doctrine\DBAL\ArrayParameterType::INTEGER</code>. <br />Now Doctrine knows how to handle the types in the array while binding the values.</p>



<span id="more-3381"></span>



<p>Example:</p>


<pre class="wp-block-code"><span><code class="hljs language-php">$sql = <span class="hljs-string">'SELECT * FROM items WHERE id IN (?)'</span>; 
$numbersAsStrings = <span class="hljs-string">"1,2,3"</span>;
$numbers = array_map(<span class="hljs-string">'intval'</span>, explode(<span class="hljs-string">','</span>, $numbersAsStrings));
$users = $con-&gt;fetchAllAssociative(
  $sql, 
  &#91;$numbers], 
  &#91;ArrayParameterType::INTEGER]
);</code></span></pre>


<p>If you omit the types, Doctrine will simply try to bind the array as string, which will fail and you get this error:<br /><em>Array to string conversion</em></p>



<p>If you try to pass it as a comma-separated string yourself and omit the types like <kbd><code>$numbers = implode(',',array_map('intval', explode(',', $numbersAsStrings)));</code></kbd> You will get an error because Doctrine expects an array of values for the <kbd>IN</kbd> clause and not a string.<br />Error: <br /><em>count(): Argument #1 ($value) must be of type Countable|array, string given</em></p>



<p>I hope this helps someone finding out how to make <kbd>WHERE IN</kbd> SQL queries with Doctrine more quickly than me next time.</p>



<p></p>The post <a href="https://nerdpress.org/2025/01/17/doctrine-where-in-with-array-of-integers/">Doctrine: WHERE IN with array of integers</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Typed Arrays with PHPStan types</title>
		<link>https://nerdpress.org/2025/01/04/typed-arrays-with-phpstan-types/</link>
		
		<dc:creator><![CDATA[Ivo Bathke]]></dc:creator>
		<pubDate>Sat, 04 Jan 2025 16:54:36 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[PHPStan]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=3367</guid>

					<description><![CDATA[<p>When dealing with legacy data, you often encounter arrays or associative arrays. These arrays are untyped, which PHPStan, of course, does not accept, resulting in numerous PHPStan errors. PHPStan, by the way, is a static analysis tool for PHP that enforces strict typing and checks for compliance with PHPDoc annotations, ensuring code is robust and &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2025/01/04/typed-arrays-with-phpstan-types/" class="more-link">Continue reading<span class="screen-reader-text"> "Typed Arrays with PHPStan types"</span></a></p>
The post <a href="https://nerdpress.org/2025/01/04/typed-arrays-with-phpstan-types/">Typed Arrays with PHPStan types</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p>When dealing with legacy data, you often encounter arrays or associative arrays. These arrays are untyped, which PHPStan, of course, does not accept, resulting in numerous PHPStan errors.</p>



<p><a href="https://phpstan.org/" target="_blank" rel="noopener" title="">PHPStan</a>, by the way, is a static analysis tool for PHP that enforces strict typing and checks for compliance with PHPDoc annotations, ensuring code is robust and maintainable.<br />For any serious project you should use it.</p>



<p>For this code:</p>


<pre class="wp-block-code"><span><code class="hljs language-php">$array = <span class="hljs-keyword">$this</span>-&gt;getUntypedArray();
$res = <span class="hljs-keyword">$this</span>-&gt;funcWithInt($array&#91;<span class="hljs-string">'number'</span>]);
...
<span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">funcWithInt</span><span class="hljs-params">(int $number)</span>:<span class="hljs-title">int</span> </span>{
    <span class="hljs-keyword">return</span> $number++;
}</code></span></pre>


<p>A typical error could be:</p>


<pre class="wp-block-code"><span><code class="hljs language-php">Parameter <span class="hljs-comment">#1 $number of method TestClass::funcWithInt expects int, mixed given.</span></code></span></pre>


<span id="more-3367"></span>



<p>You can try to typecast the value:</p>


<pre class="wp-block-code"><span><code class="hljs language-php">$res = <span class="hljs-keyword">$this</span>-&gt;funcWithInt((int)$array&#91;<span class="hljs-string">'number'</span>]);</code></span></pre>


<p>But PHPStan will not accept this and will yield this error:<br /><br /><code>Cannot cast mixed to int.</code></p>



<p>To type these untyped arrays, you can use <a href="https://phpstan.org/writing-php-code/phpdoc-types" target="_blank" rel="noopener" title="">PHPStan types</a>. These types can be defined in a PHP DocBlock, and PHPStan will use them and the error will be gone.<br />You can also reuse the types throughout the code in this class.</p>



<p>Note that you need to define the PHPStan type in a DocBlock <strong>outside</strong> of the class. <br />If defined inside the class or in a function, it will not be recognized by PHPStan!<br /><br />Example (see also here: <a href="https://phpstan.org/r/4ff8a134-4494-46e1-a026-1d5a741289e7">https://phpstan.org/r/4ff8a134-4494-46e1-a026-1d5a741289e7</a>):</p>


<pre class="wp-block-code"><span><code class="hljs language-xml"><span class="php"><span class="hljs-meta">&lt;?php</span> <span class="hljs-keyword">declare</span>(strict_types = <span class="hljs-number">1</span>);

<span class="hljs-comment">/**
 * <span class="hljs-doctag">@phpstan</span>-type Test array{
 *     string: string,
 *     number: int,
 *     array?: array{title: string}
 * }
 */</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ExampleClass</span>
</span>{
    <span class="hljs-comment">/**
     * <span class="hljs-doctag">@return</span> Test
     */</span>
    <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getArray</span><span class="hljs-params">()</span>: <span class="hljs-title">array</span>
    </span>{
        <span class="hljs-keyword">return</span> &#91;<span class="hljs-string">'string'</span> =&gt; <span class="hljs-string">'test'</span>, <span class="hljs-string">'number'</span> =&gt; <span class="hljs-number">42</span>, <span class="hljs-string">'array'</span> =&gt; &#91;<span class="hljs-string">'title'</span> =&gt; <span class="hljs-string">'title'</span>]];
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">processArray</span><span class="hljs-params">()</span>: <span class="hljs-title">void</span>
    </span>{
        $array = <span class="hljs-keyword">$this</span>-&gt;getArray();
        $res = <span class="hljs-keyword">$this</span>-&gt;funcWithInt($array&#91;<span class="hljs-string">'number'</span>]);

	<span class="hljs-comment">/** Test $anotherArray */</span>
    	$anotherArray = &#91;<span class="hljs-string">'string'</span> =&gt; <span class="hljs-string">'test2'</span>, <span class="hljs-string">'number'</span> =&gt; <span class="hljs-number">66</span>];
	$res = <span class="hljs-keyword">$this</span>-&gt;funcWithInt($anotherArray&#91;<span class="hljs-string">'number'</span>]);
		
        <span class="hljs-keyword">echo</span> <span class="hljs-string">"Result: "</span> . $res;
    }

    <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">funcWithInt</span><span class="hljs-params">(int $number)</span>: <span class="hljs-title">int</span>
    </span>{
        <span class="hljs-keyword">return</span> $number + <span class="hljs-number">1</span>;
    }
}</span></code></span></pre>


<p>Now the code will pass the PHPStan audit and some other benefits come with this:<br />PHPStan types are also supported beyond PHPStan. <br />IDEs like PhpStorm can recognize and use the type definitions. <br />Additionally, PHPStan types are compatible with <a href="https://psalm.dev/" target="_blank" rel="noopener" title="">Psalm</a> (another static analysis tool which is often used in combination with PHPStan).</p>



<p>In general, I would say it is better to map arrays to typed objects, such as DTOs, but sometimes this is not practical, and in those cases, PHPStan types provide a quick way to type untyped data.</p>



<p>You can also use PHPStan types across files by importing the phpstan-type in another file.</p>



<p>To use a phpstan-type across files, you can import it using the <code>@phpstan-import-type</code> annotation in another class&#8217;s PHPDocs. <br />Note that phpstan-import-type must be placed in the class&#8217;s docblock.<br />See: <a href="https://phpstan.org/writing-php-code/phpdoc-types#local-type-aliases">https://phpstan.org/writing-php-code/phpdoc-types#local-type-aliases</a></p>



<p></p>


<pre class="wp-block-code"><span><code class="hljs language-php"><span class="hljs-comment">/**
 * <span class="hljs-doctag">@phpstan</span>-import-type Test from ExampleClass
 */</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">OtherClass</span>
</span>{
  <span class="hljs-comment">/**
  * <span class="hljs-doctag">@param</span> Test&#91;] $test
  */</span>
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">doSomething</span><span class="hljs-params">(array $test)</span> </span>{
    ...
  }
}</code></span></pre>The post <a href="https://nerdpress.org/2025/01/04/typed-arrays-with-phpstan-types/">Typed Arrays with PHPStan types</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Deploy local build with Deployer7</title>
		<link>https://nerdpress.org/2024/11/08/deploy-local-build-with-deployer7/</link>
		
		<dc:creator><![CDATA[Ivo Bathke]]></dc:creator>
		<pubDate>Fri, 08 Nov 2024 12:38:46 +0000</pubDate>
				<category><![CDATA[Deployment]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Deployer]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=3347</guid>

					<description><![CDATA[<p>Deployer is a great tool to deploy your PHP Project. Deployer executes a set of commands on the target server to build your project and enable the newly built version. A typical deployment process with Deployer involves SSHing into the target machine, where it performs a Git checkout of the project, installs dependencies via Composer, &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2024/11/08/deploy-local-build-with-deployer7/" class="more-link">Continue reading<span class="screen-reader-text"> "Deploy local build with Deployer7"</span></a></p>
The post <a href="https://nerdpress.org/2024/11/08/deploy-local-build-with-deployer7/">Deploy local build with Deployer7</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p><br /><a href="https://deployer.org/" target="_blank" rel="noopener" title="">Deployer</a> is a great tool to deploy your PHP Project.</p>



<p>Deployer executes a set of commands on the target server to build your project and enable the newly built version. A typical deployment process with Deployer involves SSHing into the target machine, where it performs a Git checkout of the project, installs dependencies via Composer, runs build commands, and possibly triggers some database migrations. When everything is successful, it will symlink the webroot to the new release.</p>



<p>On some servers, however, there are limitations that make this process unfeasible. For instance, you can&#8217;t install Composer, Git isn&#8217;t available, the CLI PHP version is different and can&#8217;t be changed, or certain asset-building processes aren&#8217;t possible because Node.js isn&#8217;t installed. This is often the case with shared hosting.</p>



<span id="more-3347"></span>



<p>The strategy, therefore, is to build the project locally and upload it to the target machine. <br />In Deployer 6 there was a buildIn mechanism for building your project on the local host.<br />However with Deployer version 7 this has been removed and building your project locally has become a bit more fiddly. <br /><br />With Deployer 6 you could just use the <code>local</code>() method on a task and set the <code>deploy_path</code> to a local dir.</p>


<pre class="wp-block-code"><span><code class="hljs language-php">task(<span class="hljs-string">'build'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">()</span> </span>{
    set(<span class="hljs-string">'deploy_path'</span>, <span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">'/.build'</span>);
    invoke(<span class="hljs-string">'deploy:prepare'</span>);
    invoke(<span class="hljs-string">'deploy:release'</span>);
    invoke(<span class="hljs-string">'deploy:update_code'</span>);
    invoke(<span class="hljs-string">'deploy:vendors'</span>);
    invoke(<span class="hljs-string">'deploy:clear_paths'</span>);
    invoke(<span class="hljs-string">'deploy:symlink'</span>);
})-&gt;local();</code></span></pre>


<p>Then you could combine this with an upload task in a deploy task an you are done.</p>


<pre class="wp-block-code"><span><code class="hljs language-php">task(<span class="hljs-string">'upload'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">()</span> </span>{
     upload(<span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">"/.build/current/"</span>, <span class="hljs-string">'{{release_path}}'</span>, &#91;<span class="hljs-string">'--links'</span>]);
 });

 task(<span class="hljs-string">'deploy'</span>, &#91;
     <span class="hljs-string">'build'</span>,
     <span class="hljs-string">'upload'</span>,
     <span class="hljs-string">'cleanup'</span>,
     <span class="hljs-string">'success'</span>
 ]);</code></span></pre>


<p>See here for the docs of Deployer 6: <br /><a href="https://deployer.org/docs/6.x/advanced/deploy-strategies#build-server" target="_blank" rel="noopener" title="">https://deployer.org/docs/6.x/advanced/deploy-strategies#build-server</a></p>



<p>Since Deployer 7, however, the <code>local()</code> method was removed. Instead, there is now a <code>localhost()</code> method, which defines a host for local builds. Additionally, you still need to define the remote host as the target destination for the upload. <br />However, you cannot specify a single task to run on a specific host, and therefore you cannot combine tasks for <code>localhost</code> and the remote host within a single task.</p>



<p>To build the project, we now need two separate tasks. <br />I managed to make it work with two tasks and two hosts: one for local build and one that takes the fresh local build and uploads it to the remote host.</p>


<pre class="wp-block-code"><span><code class="hljs language-php">localhost(<span class="hljs-string">'local'</span>)-&gt;set(<span class="hljs-string">'deploy_path'</span>, <span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">'/.build'</span>);

host(<span class="hljs-string">'remote'</span>)
    -&gt;setHostname(<span class="hljs-string">'server-host.com'</span>)
    -&gt;set(<span class="hljs-string">'remote_user'</span>, <span class="hljs-string">'user'</span>)
    -&gt;set(<span class="hljs-string">'deploy_path'</span>, <span class="hljs-string">'/www/blog'</span>)
    -&gt;set(<span class="hljs-string">'http_user'</span>, <span class="hljs-string">'user'</span>);
....

task(<span class="hljs-string">'build'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">()</span> </span>{
    <span class="hljs-comment">// build it</span>
})-&gt;once();

task(<span class="hljs-string">'upload'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">()</span> </span>{
    upload(<span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">"/.build/current/"</span>, <span class="hljs-string">'{{deploy_path}}'</span>, &#91;<span class="hljs-string">'--links'</span>]);
});</code></span></pre>


<p>Then I simply run two separate commands: one for building on the local host and one for uploading to the remote host.</p>


<pre class="wp-block-code"><span><code class="hljs">vendor/bin/dep build local
vendor/bin/dep upload remote</code></span></pre>


<p>Not as convenient as previously but it works. :)</p>



<p>See this issue for context:  <a href="https://github.com/deployphp/deployer/issues/2838" target="_blank" rel="noopener" title="">https://github.com/deployphp/deployer/issues/2838</a></p>The post <a href="https://nerdpress.org/2024/11/08/deploy-local-build-with-deployer7/">Deploy local build with Deployer7</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Symfony integration tests custom header is missing</title>
		<link>https://nerdpress.org/2024/06/14/symfony-integration-tests-custom-header-is-missing/</link>
		
		<dc:creator><![CDATA[Ivo Bathke]]></dc:creator>
		<pubDate>Fri, 14 Jun 2024 14:27:00 +0000</pubDate>
				<category><![CDATA[Symfony]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=3316</guid>

					<description><![CDATA[<p>I am writing an integration test in Symfony with a request that includes custom headers. However, the request fails because the custom header is apparently missing. What happened was I forgot to add an HTTP prefix to the custom header. This is a common pitfall when writing integration tests in Symfony and using custom HTTP &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2024/06/14/symfony-integration-tests-custom-header-is-missing/" class="more-link">Continue reading<span class="screen-reader-text"> "Symfony integration tests custom header is missing"</span></a></p>
The post <a href="https://nerdpress.org/2024/06/14/symfony-integration-tests-custom-header-is-missing/">Symfony integration tests custom header is missing</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p>I am writing an integration test in Symfony with a request that includes custom headers. However, the request fails because the custom header is apparently missing. <br />What happened was I forgot to add an HTTP prefix to the custom header.</p>



<p>This is a common pitfall when writing integration tests in Symfony and using custom HTTP headers: the necessity to add an <strong>HTTP_</strong> prefix to the header in the test. <br />If you do not add the prefix to the custom header, it will silently not be added to the request, and you have to debug why the request fails.</p>



<p>So this will fail:</p>


<pre class="wp-block-code"><span><code class="hljs language-php"><span class="hljs-keyword">static</span>::$client-&gt;request(<span class="hljs-string">'GET'</span>, <span class="hljs-string">'/ping'</span>, &#91;], &#91;], &#91;<span class="hljs-string">'X-Custom-Header'</span> =&gt; <span class="hljs-string">'custom'</span>]);</code></span></pre>


<p>And this will work:</p>


<pre class="wp-block-code"><span><code class="hljs language-php"><span class="hljs-keyword">static</span>::$client-&gt;request(<span class="hljs-string">'GET'</span>, <span class="hljs-string">'/ping'</span>, &#91;], &#91;], &#91;<span class="hljs-string">'HTTP_X-Custom-Header'</span> =&gt; <span class="hljs-string">'custom'</span>]);</code></span></pre>


<span id="more-3316"></span>



<p>According to the <a href="https://symfony.com/doc/current/testing.html#sending-custom-headers" target="_blank" rel="noopener" title="">documentation</a>, you would actually also need to transform it to uppercase and replace the dash with an underscore but it will work with just the HTTP prefix.</p>



<p>HTTP_X_CUSTOM_HEADER</p>



<p>The prefix is however not necessary for not custom but standard headers like &#8216;Content-Type&#8217;.</p>



<p>This is not an intuitive requirement, and I wish it were not necessary. <br />At the very least, a warning would be nice when the test client encounters a non-prefixed, non-standard header.</p>



<p>A related StackOverflow Post: <a href="https://stackoverflow.com/questions/11549672/symfony-functional-test-custom-headers-not-passing-through" target="_blank" rel="noopener" title="">https://stackoverflow.com/questions/11549672/symfony-functional-test-custom-headers-not-passing-through</a></p>The post <a href="https://nerdpress.org/2024/06/14/symfony-integration-tests-custom-header-is-missing/">Symfony integration tests custom header is missing</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Composer bump</title>
		<link>https://nerdpress.org/2023/08/21/composer-bump/</link>
		
		<dc:creator><![CDATA[Ivo Bathke]]></dc:creator>
		<pubDate>Mon, 21 Aug 2023 09:36:06 +0000</pubDate>
				<category><![CDATA[Composer]]></category>
		<category><![CDATA[composer]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=3265</guid>

					<description><![CDATA[<p>Do you miss that the version numbers of your PHP dependencies are automatically updated in the composer.json file after a composer update?Just like npm or yarn are updating the version numbers in the package.json file. Then upgrade to Composer 2.4 and say hi to composer dump.This version introduced a new command composer bump which will &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2023/08/21/composer-bump/" class="more-link">Continue reading<span class="screen-reader-text"> "Composer bump"</span></a></p>
The post <a href="https://nerdpress.org/2023/08/21/composer-bump/">Composer bump</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p>Do you miss that the version numbers of your PHP dependencies are automatically updated in the composer.json file after a <code>composer update</code>?<br />Just like npm or yarn are updating the version numbers in the package.json file.</p>



<p>Then upgrade to Composer 2.4 and say hi to<a href="https://getcomposer.org/doc/03-cli.md#bump" target="_blank" rel="noopener" title=""> <code>composer dump</code></a>.<br />This version introduced a new command <code>composer bump</code> which will update your composer.json file to the precise version which is pinned in the composer.lock file.<br />It basically will sync the composer.json with the composer.lock versions and will keep the caret version constraints, so you can still make minor or patch version upgrades.</p>



<span id="more-3265"></span>



<p>This will have the effect that the version constraints are hardenend and you will not be able to install versions lower than the currently installed version.<br />With not updated versions in composer.json file you will not have a precise version base and could install lower versions than actually required.</p>



<p>Another benefit is that you can more easily <em>read</em> the currently installed version number of your dependencies.<br />Otherwise you would need to grep through the composer.lock file which is much less readable and cumbersome.<br />Some IDEs like PHPSTORM are helping here though by adding the installed version behind the dependency constraint in the composer.json file in the editor pane (see below).</p>



<p>Before composer bump:</p>



<figure class="wp-block-image size-full"><a href="https://nerdpress.org/wp-content/uploads/2023/08/composer-version-constraints-before-bump.png"><img fetchpriority="high" decoding="async" width="447" height="201" src="https://nerdpress.org/wp-content/uploads/2023/08/composer-version-constraints-before-bump.png" alt="" class="wp-image-3266" srcset="https://nerdpress.org/wp-content/uploads/2023/08/composer-version-constraints-before-bump.png 447w, https://nerdpress.org/wp-content/uploads/2023/08/composer-version-constraints-before-bump-300x135.png 300w" sizes="(max-width: 447px) 100vw, 447px" /></a><figcaption class="wp-element-caption">Not updated version constraints after composer update.</figcaption></figure>



<p>After composer bump:</p>



<figure class="wp-block-image size-full"><a href="https://nerdpress.org/wp-content/uploads/2023/08/composer-version-constraints-after-bump.png"><img decoding="async" width="437" height="182" src="https://nerdpress.org/wp-content/uploads/2023/08/composer-version-constraints-after-bump.png" alt="" class="wp-image-3267" srcset="https://nerdpress.org/wp-content/uploads/2023/08/composer-version-constraints-after-bump.png 437w, https://nerdpress.org/wp-content/uploads/2023/08/composer-version-constraints-after-bump-300x125.png 300w" sizes="(max-width: 437px) 100vw, 437px" /></a><figcaption class="wp-element-caption">Updated version constraints after composer bump.</figcaption></figure>



<p>Note that <code>composer dump</code> is benefical on projects but should be avoided in libraries because it could accidently limit the versions in which can you use the library.</p>



<p><br />Also it might be a good idea to have a option on <code>composer update</code> to integrate composer bump functionality.</p>The post <a href="https://nerdpress.org/2023/08/21/composer-bump/">Composer bump</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Testing PDF creation with headless chrome and PHP</title>
		<link>https://nerdpress.org/2023/07/28/testing-pdf-creation-with-headless-chrome-and-php/</link>
		
		<dc:creator><![CDATA[Ivo Bathke]]></dc:creator>
		<pubDate>Fri, 28 Jul 2023 09:56:50 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Minicli]]></category>
		<category><![CDATA[pdf]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=3249</guid>

					<description><![CDATA[<p>I had the task the other day to use a headless chrome to generate PDF files from websites in a PHP app. The plan was to use chrome-php with a headless chrome to generate the PDF. Usually you would install chrome/chromium on a linux server via apt and just run chrome from the PATH with &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2023/07/28/testing-pdf-creation-with-headless-chrome-and-php/" class="more-link">Continue reading<span class="screen-reader-text"> "Testing PDF creation with headless chrome and PHP"</span></a></p>
The post <a href="https://nerdpress.org/2023/07/28/testing-pdf-creation-with-headless-chrome-and-php/">Testing PDF creation with headless chrome and PHP</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p>I had the task the other day to use a headless chrome to generate PDF files from websites in a PHP app.</p>



<p>The plan was to use <a href="https://github.com/chrome-php/chrome" target="_blank" rel="noopener" title="">chrome-php</a> with a headless chrome to generate the PDF.</p>



<p>Usually you would install chrome/chromium on a linux server via <em>apt</em> and just run chrome from the PATH with chrome.<br />Since i was on shared hosting i was not sure if this was possible since i was not allowed to run <em>apt</em> commands.<br />So i tried to use Puppeteer which ships a headless chrome executable and use just this directly.<br />I installed Puppeteer with <em>npm</em> locally and uploaded the chrome executable to the shared hosting.<br />Puppeteer will place the headless chrome in the .cache dir in your home directory, f.e.:</p>



<p><code>~/.cache/puppeteer/chrome/linux-113.0.5672.63/chrome-linux64/chrome</code></p>



<p>Upload:</p>



<p><code>scp -r ~/.cache/puppeteer/chrome/linux-113.0.5672.63/chrome-linux64 me@sharedhosting:/usr/home/test</code></p>



<span id="more-3249"></span>



<p>I also created a nice little command line tool to create the pdf with chrome-php.<br />I used <a href="https://github.com/minicli/minicli" target="_blank" rel="noopener" title="">minicli</a> which is a &#8220;minimalist, dependency-free framework for building CLI-centric PHP applications&#8221;.<br />So you can create cli commands fast and without all the clutter, exactly what i needed for this small test.<br />You can find the code on Github: <a href="https://github.com/ivoba/chrome-php-minicli" target="_blank" rel="noopener" title="">https://github.com/ivoba/chrome-php-minicli</a><br />The command is here: <a href="https://github.com/ivoba/chrome-php-minicli/blob/main/command.php" target="_blank" rel="noopener" title="">https://github.com/ivoba/chrome-php-minicli/blob/main/command.php</a></p>



<p>Then i tried to generate the pdf:<br /><code>CHROME_PATH="/usr/home/test/chrome" php command.php pdf url=https://ivo-bathke.name</code></p>



<p>However this failed because the server lacked required dependencies to run the chrome executable.<br />You can check this by running this command:</p>



<p><code>me@sharedhosting:/usr/home/test$ ldd chrome-linux64/chrome | grep not<br />libatk-1.0.so.0 =&gt; not found<br />libatk-bridge-2.0.so.0 =&gt; not found<br />libxkbcommon.so.0 =&gt; not found<br />libatspi.so.0 =&gt; not found<br />libXcomposite.so.1 =&gt; not found<br />libXrandr.so.2 =&gt; not found<br />libgbm.so.1 =&gt; not found</code></p>



<p>Ok, so it wont work with the uploaded chrome executable.<br />What a pity but still a finding.</p>



<p>In the end i contacted the support and fortunatly they were willing and able to install chromium via <em>apt</em>. Nice!<br /><br />Eventually it worked with:<br /><code>CHROME_PATH="chromium" php command.php pdf url=https://ivo-bathke.name</code></p>



<p>Pdf created :)</p>



<p></p>The post <a href="https://nerdpress.org/2023/07/28/testing-pdf-creation-with-headless-chrome-and-php/">Testing PDF creation with headless chrome and PHP</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Stream an image from Symfony controller</title>
		<link>https://nerdpress.org/2022/12/12/stream-an-image-from-symfony-controller/</link>
		
		<dc:creator><![CDATA[Ivo Bathke]]></dc:creator>
		<pubDate>Mon, 12 Dec 2022 09:26:26 +0000</pubDate>
				<category><![CDATA[Symfony]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=3190</guid>

					<description><![CDATA[<p>When sending an image/file from a Symfony controller you would usually read the file from filesystem.This you can do by passing the file path to the BinaryFileResponse class and return it in the controller, as described in the docs:https://symfony.com/doc/current/components/http_foundation.html#serving-files But in some cases you might want to create an image dynamically and return it directly &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2022/12/12/stream-an-image-from-symfony-controller/" class="more-link">Continue reading<span class="screen-reader-text"> "Stream an image from Symfony controller"</span></a></p>
The post <a href="https://nerdpress.org/2022/12/12/stream-an-image-from-symfony-controller/">Stream an image from Symfony controller</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p>When sending an image/file from a Symfony controller you would usually read the file from filesystem.<br />This you can do by passing the file path to the BinaryFileResponse class and return it in the controller, as described in the docs:<br /><a href="https://symfony.com/doc/current/components/http_foundation.html#serving-files" target="_blank" rel="noreferrer noopener">https://symfony.com/doc/current/components/http_foundation.html#serving-files</a></p>


<pre class="wp-block-code"><span><code class="hljs language-php"><span class="hljs-keyword">use</span> <span class="hljs-title">Symfony</span>\<span class="hljs-title">Component</span>\<span class="hljs-title">HttpFoundation</span>\<span class="hljs-title">BinaryFileResponse</span>;

$file = <span class="hljs-string">'path/to/file.txt'</span>;
$response = <span class="hljs-keyword">new</span> BinaryFileResponse($file);</code></span></pre>


<p>But in some cases you might want to create an image dynamically and return it directly from the controller without storing it on the filesystem.<br />So you can create your image with the GD library and directly stream the GDImage object from the symfony controller with the <em>StreamedResponse</em> class.</p>



<span id="more-3190"></span>


<pre class="wp-block-code"><span><code class="hljs language-php">$image = imagecreatetruecolor($imgWidth, $imgHeight);
<span class="hljs-comment">// do something with the image</span>
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> StreamedResponse(fn () =&gt; imagepng($image), <span class="hljs-number">200</span>, &#91;<span class="hljs-string">'Content-Type'</span> =&gt; <span class="hljs-string">'image/png'</span>]);</code></span></pre>


<p>Great that was easy, but not so obvious ;)</p>



<p>Another thing you might wonder is how to add the OpenAPI annotation when streaming images to generate a proper documentation for your endpoints.<br />Just use <em>MediaType </em>class in the Response annotation and set the format to binary.</p>


<pre class="wp-block-code"><span><code class="hljs language-php">responses: &#91;
	<span class="hljs-keyword">new</span> OA\Response(
		response: <span class="hljs-number">200</span>,
		description: <span class="hljs-string">"dynamic image"</span>,
		content: <span class="hljs-keyword">new</span> OA\MediaType(
			mediaType: <span class="hljs-string">'image/png'</span>, 
			schema: <span class="hljs-keyword">new</span> OA\Schema(
				type: <span class="hljs-string">'string'</span>,
				format: <span class="hljs-string">'binary'</span>
			)
		)
	),
]</code></span></pre>


<p>Then finally you should test the <em>StreamedResponse</em> from the controller.<br />For this you can not use the normal <code>$client-&gt;getResponse()</code> but instead use the <em>getInternalResponse</em> which holds the binary data as string however.<br />So to test the binary response in your test add this:</p>


<pre class="wp-block-code"><span><code class="hljs language-php">$response = <span class="hljs-keyword">static</span>::$client-&gt;getInternalResponse();
$image = $response-&gt;getContent();
		
<span class="hljs-keyword">self</span>::assertInstanceOf(GdImage::class, imagecreatefromstring($image));</code></span></pre>


<p>This seems to be valid from Symfony 4 upwards.<br />Before Symfony 4 you could get the binary data from <code>$client-&gt;getResponse()</code>.</p>The post <a href="https://nerdpress.org/2022/12/12/stream-an-image-from-symfony-controller/">Stream an image from Symfony controller</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
