<?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/tag/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>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>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>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>Class &#8220;Psr\Log\Test\TestLogger&#8221; not found</title>
		<link>https://nerdpress.org/2022/03/04/class-psrlogtesttestlogger-not-found/</link>
		
		<dc:creator><![CDATA[Ivo Bathke]]></dc:creator>
		<pubDate>Fri, 04 Mar 2022 09:51:23 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[logger]]></category>
		<category><![CDATA[psr-3]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=3114</guid>

					<description><![CDATA[<p>The psr/log package used to have not only the Interface for PSR-3 Logger, but also actual implementations of the interface like the TestLogger.The TestLogger could be used as mock for any PSR-3 Logger in your test cases. However from v3 the TestLogger was removed, so that the psr/log package would focus solely on the Interface. &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2022/03/04/class-psrlogtesttestlogger-not-found/" class="more-link">Continue reading<span class="screen-reader-text"> "Class &#8220;Psr\Log\Test\TestLogger&#8221; not found"</span></a></p>
The post <a href="https://nerdpress.org/2022/03/04/class-psrlogtesttestlogger-not-found/">Class “Psr\Log\Test\TestLogger” not found</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p>The <a href="https://github.com/php-fig/log" target="_blank" rel="noreferrer noopener">psr/log</a> package used to have not only the Interface for PSR-3 Logger, but also actual implementations of the interface like the TestLogger.<br />The TestLogger could be used as mock for any PSR-3 Logger in your test cases.</p>



<p>However from v3 the <a href="https://github.com/php-fig/log/commit/8beb7546e5f563f30f3cff139b88e5dd512bed34" target="_blank" rel="noreferrer noopener">TestLogger was removed</a>, so that the psr/log package would focus solely on the Interface.</p>



<p>If you used the TestLogger in your project and you or some of your dependencies upgraded <strong>psr/log</strong> to &gt;= v3 you most likely saw this error:</p>



<p><code>Class "Psr\Log\Test\TestLogger" not found</code></p>



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



<p>Thanks to the community there is already a replacement for the TestLogger:<br />So just install the <a href="https://github.com/colinodell/psr-testlogger" target="_blank" rel="noreferrer noopener">https://github.com/colinodell/psr-testlogger</a> and replace the definition:</p>



<p><code>Psr\Log\Test\TestLogger:</code></p>



<p>with</p>



<p><code>ColinODell\PsrTestLogger\TestLogger:</code></p>



<p>in your code and you are good to go.</p>



<p>One file libs for the win :)</p>The post <a href="https://nerdpress.org/2022/03/04/class-psrlogtesttestlogger-not-found/">Class “Psr\Log\Test\TestLogger” not found</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>EU/VAT &#8211; Mehrwertsteuer Madness</title>
		<link>https://nerdpress.org/2021/03/30/eu-vat-mehrwertsteuer-monkey-madness/</link>
		
		<dc:creator><![CDATA[Max Girkens]]></dc:creator>
		<pubDate>Tue, 30 Mar 2021 19:13:57 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[tools]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=2995</guid>

					<description><![CDATA[<p>Calculating European VAT Rates with PHP &#8211; the easy way. Disclaimer: No legal advice, just a little experience report. If you sell digital things (services, digital goods etc.) to EU countries, you might have to calculate the VAT at the rate of the customer&#8217;s country (and even pay it there afterwards).The latter is even the &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2021/03/30/eu-vat-mehrwertsteuer-monkey-madness/" class="more-link">Continue reading<span class="screen-reader-text"> "EU/VAT &#8211; Mehrwertsteuer Madness"</span></a></p>
The post <a href="https://nerdpress.org/2021/03/30/eu-vat-mehrwertsteuer-monkey-madness/">EU/VAT – Mehrwertsteuer Madness</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<h2 class="wp-block-heading">Calculating European VAT Rates with PHP &#8211; the easy way.</h2>



<p><strong>Disclaimer: No legal advice, just a little experience report.</strong></p>



<p>If you sell digital <em>things</em> (services, digital goods etc.) to EU countries, you might have to calculate the VAT at the rate of the customer&#8217;s country (and even pay it there afterwards).<br />The latter is even the more inconvenient part, but at least the first part can be done quite easily with good old PHP.</p>



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



<p>Originally released as a Laravel plugin (or whatever it&#8217;s called in their bubble..), there is this very handy library that does the whole complex calculation starting from the seller&#8217;s country:</p>



<p><a href="https://github.com/driesvints/vat-calculator">https://github.com/driesvints/vat-calculator</a></p>



<p>Even more interesting for my purposes was this fork here:</p>



<p><a href="https://github.com/spaze/vat-calculator/">https://github.com/spaze/vat-calculator/</a></p>



<p>Works on the one hand as a complete standalone version, and still includes updates and is actually also rather a further development than a fork :thuink:<br />With this then the VAT can be calculated in a few easy calls and life is exceptionally easy.<br />Not only the country of the customer is taken into account, but also the postal code, because in Germany alone there are several different VAT rates, depending on the <a href="https://de.wikipedia.org/wiki/Helgoland" title="https://de.wikipedia.org/wiki/Helgoland">island</a> you happen to find yourself on.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: php; title: ; notranslate">
// Easy to use!
use Spaze\VatCalculator\VatCalculator;

$vatRates = new VatRates();
$vatCalculator = new VatCalculator($vatRates);
$vatCalculator-&gt;calculate(71.00, &#039;DE&#039; /* $countryCode */, &#039;41352&#039; /* $postalCode or null */,  true /* Whether the customer you&#039;re calculating the VAT for is a company */);
$vatCalculator-&gt;getTaxRateForLocation(&#039;NL&#039;);
// Check validity of a VAT number
$vatCalculator-&gt;isValidVatNumber(&#039;NL123456789B01&#039;);
</pre></div>


<p>With the appropriate VAT identification number, the annoyance of the collection is then also accordingly omitted.</p>



<p>So nice one, definitely saved me a lot of worries. Github-starred &#8211; Cheers guys!</p>The post <a href="https://nerdpress.org/2021/03/30/eu-vat-mehrwertsteuer-monkey-madness/">EU/VAT – Mehrwertsteuer Madness</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Create Google XML (Image) Sitemaps in PHP</title>
		<link>https://nerdpress.org/2012/10/20/create-google-xml-image-sitemaps-in-php/</link>
					<comments>https://nerdpress.org/2012/10/20/create-google-xml-image-sitemaps-in-php/#comments</comments>
		
		<dc:creator><![CDATA[Max Girkens]]></dc:creator>
		<pubDate>Sat, 20 Oct 2012 17:09:06 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[xml sitemaps]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=2260</guid>

					<description><![CDATA[<p>Ever wanted to create a Google XML Image Sitemap? This is how it could be done with PHP: That&#8217;s about it. I found it kind of tricky to figure out how to use googles namespaces when working with SimpleXML, so hopefully this might help someone :)</p>
The post <a href="https://nerdpress.org/2012/10/20/create-google-xml-image-sitemaps-in-php/">Create Google XML (Image) Sitemaps in PHP</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p>Ever wanted to create a Google XML <a title="Image Sitemaps" href="http://support.google.com/webmasters/bin/answer.py?hl=en&amp;answer=178636">Image Sitemap</a>?</p>
<p>This is how it could be done with PHP:<span id="more-2260"></span><br /><script src="https://gist.github.com/3924036.js"></script><noscript><pre><code class="language-php php">header(&quot;Content-type: text/xml; charset=utf-8&quot;);

//create your XML document, using the namespaces
$urlset = new SimpleXMLElement(&#039;&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&lt;urlset xmlns=&quot;http://www.sitemaps.org/schemas/sitemap/0.9&quot; xmlns:image=&quot;http://www.google.com/schemas/sitemap-image/1.1&quot; /&gt;&lt;!--?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?--&gt;&#039;);

//iterate over your sites pages or whatever you like

foreach (getMyPagesOrWhatEver() as $item):

    //add the page URL to the XML urlset
    $url = $urlset-&gt;addChild(&#039;url&#039;);
    $url-&gt;addChild(&#039;loc&#039;, $item-&gt;URL&nbsp;);
    $url-&gt;addChild(&#039;lastmod&#039;, $item-&gt;LASTMOD );
    $url-&gt;addChild(&#039;changefreq&#039;, &#039;daily&#039;); &nbsp;//weekly etc.
    $url-&gt;addChild(&#039;priority&#039;, &#039;1.0&#039;);

    //add an image
    if ( $item-&gt;IMAGE ):
        $image = $url-&gt;addChild(&#039;image:image&#039;, null, &#039;http://www.google.com/schemas/sitemap-image/1.1&#039;);
        $image-&gt;addChild(&#039;image:loc&#039;,$item-&gt;IMAGE-&gt;URL, &#039;http://www.google.com/schemas/sitemap-image/1.1&#039;);
        $image-&gt;addChild(&#039;image:caption&#039;,$item-&gt;IMAGE-&gt;ALT_OR_TITLE&nbsp;, &#039;http://www.google.com/schemas/sitemap-image/1.1&#039;);
    endif;

endforeach;

//add whitespaces to xml output (optional, of course)
$dom = new DomDocument();
$dom-&gt;loadXML($urlset-&gt;asXML());
$dom-&gt;formatOutput = true;
//output xml

echo $dom-&gt;saveXML();</code></pre></noscript></p>
<p>That&#8217;s about it.</p>
<p>I found it kind of tricky to figure out how to use googles namespaces when working with SimpleXML, so hopefully this might help someone :)</p>The post <a href="https://nerdpress.org/2012/10/20/create-google-xml-image-sitemaps-in-php/">Create Google XML (Image) Sitemaps in PHP</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
					<wfw:commentRss>https://nerdpress.org/2012/10/20/create-google-xml-image-sitemaps-in-php/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>run JavaScript code in PHP 5.3 with the v8js extension</title>
		<link>https://nerdpress.org/2012/03/09/run-javascript-code-in-php-5-3-with-the-v8js-extension/</link>
		
		<dc:creator><![CDATA[Max Girkens]]></dc:creator>
		<pubDate>Thu, 08 Mar 2012 23:14:10 +0000</pubDate>
				<category><![CDATA[JS]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[homebrew]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[pecl]]></category>
		<category><![CDATA[v8js.so]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=2102</guid>

					<description><![CDATA[<p>&#8230;for some reason i needed to get t h i s to work before going to sleep. I&#8217;m running OSX 10.7.3 with macports which usually does the job, but  Google&#8217;s V8 Javascript Engine is not available as a port yet. So&#8230; homebrew to the rescue: installed it: and installed v8 wait, this seems too easy. &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2012/03/09/run-javascript-code-in-php-5-3-with-the-v8js-extension/" class="more-link">Continue reading<span class="screen-reader-text"> "run JavaScript code in PHP 5.3 with the v8js extension"</span></a></p>
The post <a href="https://nerdpress.org/2012/03/09/run-javascript-code-in-php-5-3-with-the-v8js-extension/">run JavaScript code in PHP 5.3 with the v8js extension</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p>&#8230;for some reason i needed to get <strong><a href="http://php.net/manual/en/book.v8js.php">t h i s</a></strong> to work before going to sleep.</p>
<p>I&#8217;m running OSX 10.7.3 with <a href="http://www.macports.org/">macports</a> which usually does the job, but  <a href="http://code.google.com/p/v8/">Google&#8217;s V8 Javascript Engine</a> is not available as a port yet.<span id="more-2102"></span></p>
<p>So&#8230; <a href="http://mxcl.github.com/homebrew/">homebrew</a> to the rescue:</p>
<p>installed it:</p>
<pre class="brush: bash; title: ; notranslate">
/usr/bin/ruby -e &quot;$(/usr/bin/curl -fsSL https://raw.github.com/gist/323731)&quot;
</pre>
<p>and installed v8</p>
<pre class="brush: bash; title: ; notranslate">
brew install v8
</pre>
<p>wait, this seems <a href="http://www.youtube.com/watch?v=jWI8w9kLAks">too easy</a>.</p>
<p>PECL installed some beta version of the PHP extension.</p>
<pre class="brush: bash; title: ; notranslate">
sudo pecl install channel://pecl.php.net/v8js-0.1.2
</pre>
<p>added</p>
<pre class="brush: bash; title: ; notranslate">extension=v8js.so</pre>
<p>to the php.ini file.</p>
<p>und bitteschön:</p>
<pre class="brush: php; title: ; notranslate">

$v8 = new V8Js();
var_dump($v8-&gt;executeString(&quot;

//hey i'm Javascript Code

//INSIDE PHP !

//wow.
var hello = 'Hallo';

function helloWorld( string ){
return hello + ' ' + string;
}

helloWorld('World');

&quot;));

</pre>
<p>Such things might come in handy one day, <a href="https://plus.google.com/115423703838305233565/posts/VtskgWhB3kV">you know</a>.</p>The post <a href="https://nerdpress.org/2012/03/09/run-javascript-code-in-php-5-3-with-the-v8js-extension/">run JavaScript code in PHP 5.3 with the v8js extension</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>PHP odds! today: pass by reference traps</title>
		<link>https://nerdpress.org/2011/08/25/php-odds-today-pass-by-reference-traps/</link>
					<comments>https://nerdpress.org/2011/08/25/php-odds-today-pass-by-reference-traps/#comments</comments>
		
		<dc:creator><![CDATA[Ivo Bathke]]></dc:creator>
		<pubDate>Thu, 25 Aug 2011 09:42:59 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[foreach]]></category>
		<category><![CDATA[reference]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=1590</guid>

					<description><![CDATA[<p>This is dangerous: //example array $array = array('a' =&#62; array(array(243,453,435,232))); foreach ($array&#x5B;'a'] as &#38;$value) { $value&#x5B;3] = $newvalue; } This works, but now the $value var is in the array by reference which can lateron lead to: Notice: Array to string conversion EDIT: see below in the comments for when&#8230; This works better: Using reference, &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2011/08/25/php-odds-today-pass-by-reference-traps/" class="more-link">Continue reading<span class="screen-reader-text"> "PHP odds! today: pass by reference traps"</span></a></p>
The post <a href="https://nerdpress.org/2011/08/25/php-odds-today-pass-by-reference-traps/">PHP odds! today: pass by reference traps</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p>This is dangerous:</p>
<pre class="brush: php; title: ; notranslate">
//example array
$array = array('a' =&gt; array(array(243,453,435,232)));

foreach ($array&#x5B;'a'] as &amp;$value) {
         $value&#x5B;3] = $newvalue;
}
</pre>
<p>This works, but now the $value var is in the array by reference which can lateron lead to:<br />
<strong>Notice: Array to string conversion</strong><br />
EDIT:<br />
see below in the comments for when&#8230;</p>
<p>This works better:</p>
<p><span id="more-1590"></span></p>
<p>Using reference, array_walk und lambda functions works:</p>
<pre class="brush: php; title: ; notranslate">
array_walk($array&#x5B;$key], function(&amp;$value, $key, $newvalue) {
                                   $value&#x5B;3] = $newvalue;
                          },$newvalue);
//works
</pre>
<p>or that (thanks lars):</p>
<pre class="brush: php; title: ; notranslate">
foreach ($array&#x5B;'a'] as $k =&gt; $value) {
         $array&#x5B;'a']&#x5B;$k]&#x5B;3] = $newvalue;
}
</pre>The post <a href="https://nerdpress.org/2011/08/25/php-odds-today-pass-by-reference-traps/">PHP odds! today: pass by reference traps</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
					<wfw:commentRss>https://nerdpress.org/2011/08/25/php-odds-today-pass-by-reference-traps/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
	</channel>
</rss>
