<?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>DDEV Archives - Víctor Dorado</title>
	<atom:link href="https://www.victordorado.es/category/ddev/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.victordorado.es/category/ddev/</link>
	<description></description>
	<lastBuildDate>Tue, 05 May 2026 07:33:40 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0.2</generator>
	<item>
		<title>Bring the AWS Cloud to Your Desktop with LocalStack™, DDEV and Drupal</title>
		<link>https://www.victordorado.es/2024/12/19/bring-the-aws-cloud-to-your-desktop-with-localstack-and-drupal/</link>
					<comments>https://www.victordorado.es/2024/12/19/bring-the-aws-cloud-to-your-desktop-with-localstack-and-drupal/#comments</comments>
		
		<dc:creator><![CDATA[vidorado]]></dc:creator>
		<pubDate>Thu, 19 Dec 2024 19:07:56 +0000</pubDate>
				<category><![CDATA[DDEV]]></category>
		<category><![CDATA[Drupal]]></category>
		<guid isPermaLink="false">https://www.victordorado.es/?p=2323</guid>

					<description><![CDATA[<p>Developing applications to run on AWS can become much simpler with a local setup. We will perform a complete S3 local service installation with Drupal and DDEV</p>
<p>The post <a href="https://www.victordorado.es/2024/12/19/bring-the-aws-cloud-to-your-desktop-with-localstack-and-drupal/">Bring the AWS Cloud to Your Desktop with LocalStack™, DDEV and Drupal</a> appeared first on <a href="https://www.victordorado.es">Víctor Dorado</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph"><strong>Developing applications to run on AWS can become much simpler with a local setup</strong>. Imagine working without needing an internet connection or incurring additional costs. As LocalStack states on its website:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><em>&#8220;Develop and test your AWS applications locally to reduce development time and increase product velocity. Reduce unnecessary AWS spend and remove the complexity and risk of maintaining AWS dev accounts.&#8221;</em></p>
</blockquote>



<p class="wp-block-paragraph">LocalStack™ offers both a community and a pro version, allowing users to access the community version for free. In this guide, we’ll walk you through configuring a local instance of Simple Storage Service (S3) hosted in a DDEV container.</p>



<h2 class="wp-block-heading">Installing Drupal with DDEV</h2>



<p class="wp-block-paragraph">From the <a href="https://ddev.readthedocs.io/en/stable/users/quickstart/#drupal">DDEV quickstart guide</a>, we can follow these steps to set up a new Drupal 11 project:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>mkdir my-drupal11-site &amp;&amp; cd my-drupal11-site \
ddev config --project-type=drupal11 --docroot=web \
ddev composer create drupal/recommended-project:^11.0.0 \
ddev composer require drush/drush \
ddev drush site:install --account-name=admin --account-pass=admin -y \
ddev launch</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #88C0D0">mkdir</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">my-drupal11-site</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&amp;&amp;</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">cd</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">my-drupal11-site</span><span style="color: #D8DEE9FF"> </span><span style="color: #EBCB8B">\</span></span>
<span class="line"><span style="color: #D8DEE9FF">ddev </span><span style="color: #A3BE8C">config</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">--project-type=drupal11</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">--docroot=web</span><span style="color: #D8DEE9FF"> </span><span style="color: #EBCB8B">\</span></span>
<span class="line"><span style="color: #D8DEE9FF">ddev </span><span style="color: #A3BE8C">composer</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">create</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">drupal/recommended-project:^11.0.0</span><span style="color: #D8DEE9FF"> </span><span style="color: #EBCB8B">\</span></span>
<span class="line"><span style="color: #D8DEE9FF">ddev </span><span style="color: #A3BE8C">composer</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">require</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">drush/drush</span><span style="color: #D8DEE9FF"> </span><span style="color: #EBCB8B">\</span></span>
<span class="line"><span style="color: #D8DEE9FF">ddev </span><span style="color: #A3BE8C">drush</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">site:install</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">--account-name=admin</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">--account-pass=admin</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">-y</span><span style="color: #D8DEE9FF"> </span><span style="color: #EBCB8B">\</span></span>
<span class="line"><span style="color: #D8DEE9FF">ddev </span><span style="color: #A3BE8C">launch</span></span></code></pre></div>



<h3 class="wp-block-heading">Important:</h3>



<p class="wp-block-paragraph">At the time of writing this guide, the <strong>S3 Filesystem</strong> module requires <code>drupal/core:^11.0.0</code>. This means it is not compatible with Drupal 11.1, which is currently the latest core version. To address this, we’ve specified the <code>^11.0.0</code> version constraint in Composer, ensuring the installation of the latest 11.0.x version.</p>



<p class="wp-block-paragraph">With these steps, <strong>you should now have a DDEV project running a clean Drupal installation with Drush.</strong></p>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="641" src="https://www.victordorado.es/wp-content/uploads/2024/12/image-1-1024x641.png" alt="" class="wp-image-2333" srcset="https://www.victordorado.es/wp-content/uploads/2024/12/image-1-1024x641.png 1024w, https://www.victordorado.es/wp-content/uploads/2024/12/image-1-300x188.png 300w, https://www.victordorado.es/wp-content/uploads/2024/12/image-1-768x480.png 768w, https://www.victordorado.es/wp-content/uploads/2024/12/image-1.png 1226w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h2 class="wp-block-heading">Adding the LocalStack service to DDEV</h2>



<p class="wp-block-paragraph">Next, we need to create a file named <code>docker-compose.localstack.yml</code> in the root of the <code>.ddev</code> directory with the following content:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#39404f;color:#c8d0e0">docker-compose.localstack.yml</span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>services:
  localstack:
    container_name: ddev-${DDEV_SITENAME}-localstack
    image: localstack/localstack
    restart: always
    labels:
      com.ddev.site-name: ${DDEV_SITENAME}
      com.ddev.approot: $DDEV_APPROOT
    environment:
      # LocalStack config: https://docs.localstack.cloud/references/configuration/
      - VIRTUAL_HOST=$DDEV_HOSTNAME
      - DEBUG=0
    ports:
      - 4566:4566
    volumes:
      - "./localstack/volume:/var/lib/localstack"
      - "/var/run/docker.sock:/var/run/docker.sock"

  web:
    links:
      - localstack</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #8FBCBB">services</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #8FBCBB">localstack</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">container_name</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">ddev-${DDEV_SITENAME}-localstack</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">image</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">localstack/localstack</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">restart</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">always</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">labels</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #8FBCBB">com.ddev.site-name</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">${DDEV_SITENAME}</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #8FBCBB">com.ddev.approot</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">$DDEV_APPROOT</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">environment</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #ECEFF4">      </span><span style="color: #616E88"># LocalStack config: https://docs.localstack.cloud/references/configuration/</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">VIRTUAL_HOST=$DDEV_HOSTNAME</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">DEBUG=0</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">ports</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">4566:4566</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">volumes</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">./localstack/volume:/var/lib/localstack</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">/var/run/docker.sock:/var/run/docker.sock</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #8FBCBB">web</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">links</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">localstack</span></span></code></pre></div>



<p class="wp-block-paragraph">(based on <a href="https://docs.localstack.cloud/getting-started/installation/#docker-compose">https://docs.localstack.cloud/getting-started/installation/#docker-compose</a>)</p>



<p class="wp-block-paragraph">Then, we must<strong> restart the DDEV project:</strong></p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>ddev restart</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #88C0D0">ddev</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">restart</span></span></code></pre></div>



<p class="wp-block-paragraph">And we will be able to <strong>open a ssh connection to our new localstack container:</strong></p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>docker exec -ti ddev-drupal11-localstack-localstack bash</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #88C0D0">docker</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">exec</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">-ti</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">ddev-drupal11-localstack-localstack</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">bash</span></span></code></pre></div>



<p class="wp-block-paragraph">Following the instructions from the <a href="https://docs.localstack.cloud/user-guide/aws/s3/">LocalStack S3 User Guide</a>, we will now proceed to create a bucket:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>awslocal s3api create-bucket --bucket sample-bucket
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #88C0D0">awslocal</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">s3api</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">create-bucket</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">--bucket</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">sample-bucket</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">And we will get a positive feedback response:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>{
    "Location": "/sample-bucket"
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">&quot;Location&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">/sample-bucket</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p class="wp-block-paragraph">At this point, <strong>we now have a local AWS S3 instance up and running inside a container</strong>, accessible as <code>localstack</code> from the other DDEV project containers.</p>



<h3 class="wp-block-heading">Important Caveat:</h3>



<p class="wp-block-paragraph"><strong>The community version of the LocalStack container does not include persistence</strong>. This feature is only available in the paid LocalStack Pro version. As a result, when using the community edition, all buckets and their content will be lost each time the <code>localstack</code> container is restarted. This limitation means we can only perform very basic tests with this setup.</p>



<h2 class="wp-block-heading">Configuring EC2 Metadata Mock</h2>



<p class="wp-block-paragraph">Many AWS SDKs rely on a critical component available in every EC2 instance: a metadata server accessible at <code>169.254.169.254</code>. Since this server won&#8217;t be available from our DDEV web container, we need to mock it using a tool provided by Amazon.</p>



<p class="wp-block-paragraph"><a href="https://github.com/aws/amazon-ec2-metadata-mock">Amazon EC2 Metadata Mock</a></p>



<p class="wp-block-paragraph">Fortunately, <strong>this tool is available as a Docker image</strong>, which allows us to create another container within our DDEV project to host it. To set this up, create a file named <code>docker-compose.ec2-metadata-mock.yml</code> inside the <code>.ddev</code> directory with the following content:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#39404f;color:#c8d0e0">docker-compose.ec2-metadata-mock.yml</span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>services:
  ec2-metadata-mock:
    container_name: ddev-${DDEV_SITENAME}-ec2-metadata-mock
    image: public.ecr.aws/aws-ec2/amazon-ec2-metadata-mock:v1.12.0
    restart: always
    labels:
      com.ddev.site-name: ${DDEV_SITENAME}
      com.ddev.approot: $DDEV_APPROOT
    environment: []
    ports:
      - 1338:1338
    volumes: []

  web:
    links:
      - ec2-metadata-mock</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #8FBCBB">services</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #8FBCBB">ec2-metadata-mock</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">container_name</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">ddev-${DDEV_SITENAME}-ec2-metadata-mock</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">image</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">public.ecr.aws/aws-ec2/amazon-ec2-metadata-mock:v1.12.0</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">restart</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">always</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">labels</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #8FBCBB">com.ddev.site-name</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">${DDEV_SITENAME}</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #8FBCBB">com.ddev.approot</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">$DDEV_APPROOT</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">environment</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[]</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">ports</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">1338:1338</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">volumes</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[]</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #8FBCBB">web</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">links</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">ec2-metadata-mock</span></span></code></pre></div>



<p class="wp-block-paragraph"><strong>The tool listens on port <code>1338</code> instead of the standard port <code>80</code></strong>, but we’ll address this difference later. More details will follow in the next sections.</p>



<p class="wp-block-paragraph"><strong>Restart DDEV</strong> to deploy the new container:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>ddev restart</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #88C0D0">ddev</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">restart</span></span></code></pre></div>



<p class="wp-block-paragraph">After restarting, <strong>the EC2 Metadata Mock will be running on port <code>1338</code> and accessible as <code>ec2-metadata-mock</code> from other containers</strong> within the DDEV project.</p>



<p class="wp-block-paragraph">We now can make a simple request from our web container to http://ec2-metadata-mock:1338/</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>ddev ssh

curl http://ec2-metadata-mock:1338/latest/meta-data/iam/security-credentials
baskinc-role

curl http://ec2-metadata-mock:1338/latest/meta-data/iam/security-credentials/baskinc-role
{
  "Code": "Success",
  "LastUpdated": "2020-04-02T18:50:40Z",
  "Type": "AWS-HMAC",
  "AccessKeyId": "12345678901",
  "SecretAccessKey": "v/12345678901",
  "Token": "TEST92test48TEST+y6RpoTEST92test48TEST/8oWVAiBqTEsT5Ky7ty2tEStxC1T==",
  "Expiration": "2020-04-02T00:49:51Z"
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #88C0D0">ddev</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">ssh</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">curl</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">http://ec2-metadata-mock:1338/latest/meta-data/iam/security-credentials</span></span>
<span class="line"><span style="color: #88C0D0">baskinc-role</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">curl</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">http://ec2-metadata-mock:1338/latest/meta-data/iam/security-credentials/baskinc-role</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #88C0D0">&quot;Code&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Success</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #88C0D0">&quot;LastUpdated&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">2020-04-02T18:50:40Z</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #88C0D0">&quot;Type&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">AWS-HMAC</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #88C0D0">&quot;AccessKeyId&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">12345678901</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #88C0D0">&quot;SecretAccessKey&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">v/12345678901</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #88C0D0">&quot;Token&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">TEST92test48TEST+y6RpoTEST92test48TEST/8oWVAiBqTEsT5Ky7ty2tEStxC1T==</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #88C0D0">&quot;Expiration&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">2020-04-02T00:49:51Z</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">The <code>Expiration</code> value will trigger a warning later when validating the configuration in Drupal. However, this warning can safely be ignored.</p>



<p class="wp-block-paragraph">For more information on making requests to the EC2 Metadata Mock, refer to the official documentation at <a href="https://github.com/aws/amazon-ec2-metadata-mock/tree/main?tab=readme-ov-file#making-a-request">https://github.com/aws/amazon-ec2-metadata-mock/tree/main?tab=readme-ov-file#making-a-request</a>.</p>



<h2 class="wp-block-heading">Patch the aws-sdk-php Composer package</h2>



<p class="wp-block-paragraph">The simplest way to make the AWS SDK recognize a custom EC2 Metadata Service location is by directly <strong>modifying its hardcoded IP address:</strong></p>



<p class="wp-block-paragraph"><strong>Change the value of the class constant </strong><code>Aws\Credentials\InstanceProfileProvider::<em>DEFAULT_METADATA_SERVICE_IPv4_ENDPOINT</em></code> to <code>http://ec2-metadata-mock:1338</code></p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#39404f;color:#c8d0e0">PHP</span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>&lt;?php
namespace Aws\Credentials;

use ...

/**
 * Credential provider that provides credentials from the EC2 metadata service.
 */
class InstanceProfileProvider
{
    ...
    const DEFAULT_METADATA_SERVICE_IPv4_ENDPOINT = 'http://ec2-metadata-mock:1338';</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">&lt;?</span><span style="color: #D8DEE9FF">php</span></span>
<span class="line"><span style="color: #81A1C1">namespace</span><span style="color: #D8DEE9FF"> Aws</span><span style="color: #ECEFF4">\</span><span style="color: #D8DEE9FF">Credentials</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">use</span><span style="color: #D8DEE9FF"> ...</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">/**</span></span>
<span class="line"><span style="color: #616E88"> * Credential provider that provides credentials from the EC2 metadata service.</span></span>
<span class="line"><span style="color: #616E88"> */</span></span>
<span class="line"><span style="color: #8FBCBB">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">InstanceProfileProvider</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    ...</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">DEFAULT_METADATA_SERVICE_IPv4_ENDPOINT</span><span style="color: #D8DEE9FF"> = &#39;</span><span style="color: #8FBCBB">http</span><span style="color: #D8DEE9FF">:</span><span style="color: #616E88">//ec2-metadata-mock:1338&#39;;</span></span></code></pre></div>



<p class="wp-block-paragraph">This way, the credentials provider accessed will be our EC2 Metadata Mock server.</p>



<p class="wp-block-paragraph"><strong>You can make a composer patch to retain this configuration.</strong> See <a href="https://github.com/cweagans/composer-patches">https://github.com/cweagans/composer-patches</a> for more information.</p>



<h2 class="wp-block-heading">Edit /etc/hosts file in the host computer</h2>



<p class="wp-block-paragraph">From the web container, <strong>the LocalStack container is accessible as <code>localstack</code> via <code>http://localstack:4566</code></strong>. This setup enables internal communication between Drupal and LocalStack to function seamlessly.</p>



<p class="wp-block-paragraph">However, <strong>we also need access to the bucket from the host computer (our PC), </strong>as the web browser navigating the Drupal site will be running locally and needs to access the files stored in the bucket.</p>



<p class="wp-block-paragraph">So, <strong>add a new hostname to your localhost definition line inside your  /etc/hosts file</strong>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-blur-enabled" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#39404f;color:#c8d0e0">/etc/hosts</span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>127.0.0.1 localhost localstack
127.0.1.1 MY-PC

# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line cbp-no-blur"><span style="color: #88C0D0">127.0.0.1</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">localhost</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">localstack</span></span>
<span class="line"><span style="color: #88C0D0">127.0.1.1</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">MY-PC</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88"># The following lines are desirable for IPv6 capable hosts</span></span>
<span class="line"><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF">:1 </span><span style="color: #A3BE8C">ip6-localhost</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">ip6-loopback</span></span>
<span class="line"><span style="color: #88C0D0">fe00::0</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">ip6-localnet</span></span>
<span class="line"><span style="color: #88C0D0">ff00::0</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">ip6-mcastprefix</span></span>
<span class="line"><span style="color: #88C0D0">ff02::1</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">ip6-allnodes</span></span>
<span class="line"><span style="color: #88C0D0">ff02::2</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">ip6-allrouters</span></span></code></pre></div>



<p class="wp-block-paragraph">And that’s it! <strong>Both your web browser and the DDEV web container can now access the bucket</strong> via <code>http://localstack:4566/sample-bucket</code>.</p>



<p class="wp-block-paragraph">Remember, <strong>we selected the &#8220;Use path-style endpoint&#8221; option in the S3 FileSystem configuration</strong>, which ensures compatibility with this setup.</p>



<h2 class="wp-block-heading">Configuring Drupal</h2>



<p class="wp-block-paragraph"><strong>Install the module &#8220;S3 Filesystem&#8221;.</strong></p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>ddev composer require drupal/s3fs \
ddev drush en s3fs</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #88C0D0">ddev</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">composer</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">require</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">drupal/s3fs</span><span style="color: #D8DEE9FF"> </span><span style="color: #EBCB8B">\</span></span>
<span class="line"><span style="color: #D8DEE9FF">ddev </span><span style="color: #A3BE8C">drush</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">en</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">s3fs</span></span></code></pre></div>



<p class="wp-block-paragraph">And go to configure its settings at <code>/admin/config/media/s3fs</code>. </p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="641" src="https://www.victordorado.es/wp-content/uploads/2024/12/image-2-1024x641.png" alt="" class="wp-image-2334" srcset="https://www.victordorado.es/wp-content/uploads/2024/12/image-2-1024x641.png 1024w, https://www.victordorado.es/wp-content/uploads/2024/12/image-2-300x188.png 300w, https://www.victordorado.es/wp-content/uploads/2024/12/image-2-768x480.png 768w, https://www.victordorado.es/wp-content/uploads/2024/12/image-2.png 1226w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">We will only have to fill these settings:</p>



<ul class="wp-block-list">
<li><strong>S3 Bucket Name:</strong> sample-bucket</li>



<li><strong>Use a Custom Host:</strong> Checked</li>



<li><strong>Hostname:</strong> http://localstack:4566</li>



<li><strong>Use path-style endpoint:</strong> Checked</li>
</ul>



<p class="wp-block-paragraph">Now, you can go to the &#8220;Actions&#8221; tab or visit <code>/admin/config/media/s3fs/actions</code> and click on the &#8220;Validate configuration&#8221; button.</p>



<p class="wp-block-paragraph">Despite the warning about the EC2 Metadata Mock credentials&#8217; expiration date (set to the year 2020), <strong>the settings will still be validated correctly</strong>. This means that your Drupal configuration will function as expected, even with this warning present.</p>



<figure class="wp-block-image size-full"><img decoding="async" width="959" height="188" src="https://www.victordorado.es/wp-content/uploads/2024/12/image-4.png" alt="" class="wp-image-2341" srcset="https://www.victordorado.es/wp-content/uploads/2024/12/image-4.png 959w, https://www.victordorado.es/wp-content/uploads/2024/12/image-4-300x59.png 300w, https://www.victordorado.es/wp-content/uploads/2024/12/image-4-768x151.png 768w" sizes="(max-width: 959px) 100vw, 959px" /></figure>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="954" height="143" src="https://www.victordorado.es/wp-content/uploads/2024/12/image-5.png" alt="" class="wp-image-2342" srcset="https://www.victordorado.es/wp-content/uploads/2024/12/image-5.png 954w, https://www.victordorado.es/wp-content/uploads/2024/12/image-5-300x45.png 300w, https://www.victordorado.es/wp-content/uploads/2024/12/image-5-768x115.png 768w" sizes="(max-width: 954px) 100vw, 954px" /></figure>



<h2 class="wp-block-heading">Make an image field use the new S3 storage</h2>



<p class="wp-block-paragraph">Go to <code>/admin/structure/types/manage/article/fields/node.article.field_image</code> so <strong>we can configure the image field in the default &#8220;Article&#8221; content type</strong>:</p>



<p class="wp-block-paragraph">Then, <strong>change the Field Storage to &#8220;S3 File System&#8221; and click on &#8220;Save settings&#8221;.</strong></p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1019" height="654" src="https://www.victordorado.es/wp-content/uploads/2024/12/image-3.png" alt="" class="wp-image-2339" srcset="https://www.victordorado.es/wp-content/uploads/2024/12/image-3.png 1019w, https://www.victordorado.es/wp-content/uploads/2024/12/image-3-300x193.png 300w, https://www.victordorado.es/wp-content/uploads/2024/12/image-3-768x493.png 768w" sizes="(max-width: 1019px) 100vw, 1019px" /></figure>



<p class="wp-block-paragraph">From now on, <strong>any new images added to articles will be stored in the new LocalStack <code>sample-bucket</code></strong></p>



<p class="wp-block-paragraph"><strong>Let&#8217;s now create a new Article with an image:</strong></p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="791" src="https://www.victordorado.es/wp-content/uploads/2024/12/image-6-1024x791.png" alt="" class="wp-image-2343" srcset="https://www.victordorado.es/wp-content/uploads/2024/12/image-6-1024x791.png 1024w, https://www.victordorado.es/wp-content/uploads/2024/12/image-6-300x232.png 300w, https://www.victordorado.es/wp-content/uploads/2024/12/image-6-768x594.png 768w, https://www.victordorado.es/wp-content/uploads/2024/12/image-6.png 1255w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph"><strong>We can view the image thanks to the previously configured entry in <code>/etc/hosts</code> for the <code>localstack</code> hostname</strong>. This ensures that the image’s <code>src</code> attribute, pointing to:</p>



<pre class="wp-block-code"><code>https:&#47;&#47;&#91;ddev-project-name].ddev.site/s3/files/styles/wide/s3/2024-12/image.webp?itok=_NXZWAN7</code></pre>



<p class="wp-block-paragraph">Which is redirected with a 302 to:</p>



<pre class="wp-block-code"><code>http:&#47;&#47;localstack:4566/sample-bucket/styles/wide/s3/2024-12/localstack.png.webp?itok=_NXZWAN7</code></pre>



<p class="wp-block-paragraph">On the other hand, we can also see that <strong>the image and its image styles are effectively stored in the bucket</strong>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#39404f;color:#c8d0e0">Bash</span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>docker exec -ti ddev-drupal11-localstack-localstack bash

awslocal s3api list-objects --bucket sample-bucket

{
    "Contents": &#91;
        {
            "Key": "2024-12/localstack.png",
            "LastModified": "2024-12-19T18:26:47.000Z",
            "ETag": "\"d7cbf11f95e7d277c8989d2e8a11267f\"",
            "Size": 11510,
            "StorageClass": "STANDARD",
            "Owner": {
                "DisplayName": "webfile",
                "ID": "75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a"
            }
        },
        {
            "Key": "styles/thumbnail/s3/2024-12/localstack.png.webp",
            "LastModified": "2024-12-19T18:26:47.000Z",
            "ETag": "\"34ab21d0998bd749a7cfe82085ca6f76\"",
            "Size": 780,
            "StorageClass": "STANDARD",
            "Owner": {
                "DisplayName": "webfile",
                "ID": "75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a"
            }
        },
        {
            "Key": "styles/wide/s3/2024-12/localstack.png.webp",
            "LastModified": "2024-12-19T18:26:55.000Z",
            "ETag": "\"4f4f644fc0a98604de82e58b720af7cb\"",
            "Size": 3212,
            "StorageClass": "STANDARD",
            "Owner": {
                "DisplayName": "webfile",
                "ID": "75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a"
            }
        }
    &#93;,
    "RequestCharged": null,
    "Prefix": ""
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #88C0D0">docker</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">exec</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">-ti</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">ddev-drupal11-localstack-localstack</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">bash</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">awslocal</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">s3api</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">list-objects</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">--bucket</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">sample-bucket</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">&quot;Contents&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> &#91;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">&quot;Key&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">2024-12/localstack.png</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">&quot;LastModified&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">2024-12-19T18:26:47.000Z</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">&quot;ETag&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #EBCB8B">\&quot;</span><span style="color: #A3BE8C">d7cbf11f95e7d277c8989d2e8a11267f</span><span style="color: #EBCB8B">\&quot;</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">&quot;Size&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">11510</span><span style="color: #A3BE8C">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">&quot;StorageClass&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">STANDARD</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">&quot;Owner&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">&quot;DisplayName&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">webfile</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">&quot;ID&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">&quot;Key&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">styles/thumbnail/s3/2024-12/localstack.png.webp</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">&quot;LastModified&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">2024-12-19T18:26:47.000Z</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">&quot;ETag&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #EBCB8B">\&quot;</span><span style="color: #A3BE8C">34ab21d0998bd749a7cfe82085ca6f76</span><span style="color: #EBCB8B">\&quot;</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">&quot;Size&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">780</span><span style="color: #A3BE8C">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">&quot;StorageClass&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">STANDARD</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">&quot;Owner&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">&quot;DisplayName&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">webfile</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">&quot;ID&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        },</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">&quot;Key&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">styles/wide/s3/2024-12/localstack.png.webp</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">&quot;LastModified&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">2024-12-19T18:26:55.000Z</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">&quot;ETag&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #EBCB8B">\&quot;</span><span style="color: #A3BE8C">4f4f644fc0a98604de82e58b720af7cb</span><span style="color: #EBCB8B">\&quot;</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">&quot;Size&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">3212</span><span style="color: #A3BE8C">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">&quot;StorageClass&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">STANDARD</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">&quot;Owner&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">&quot;DisplayName&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">webfile</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">&quot;ID&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        }</span></span>
<span class="line"><span style="color: #D8DEE9FF">    &#93;,</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">&quot;RequestCharged&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">null,</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">&quot;Prefix&quot;</span><span style="color: #88C0D0">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">}</span></span></code></pre></div>



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



<p class="wp-block-paragraph"><strong>Having a local implementation of AWS services is incredibly helpful for development purposes</strong>. This applies not only to the S3 service but also to the wide range of complex services supported by LocalStack. Using LocalStack can prevent unnecessary expenses—potentially significant ones—caused by mistakes during development.</p>



<p class="wp-block-paragraph">In this guide, we focused solely on the S3 service and demonstrated the simplest configuration method. However, <strong>with a more advanced understanding of Docker Compose, it’s possible to design and implement a fully-featured local AWS cloud infrastructure </strong>to meet even the most complex development needs.</p>
<p>The post <a href="https://www.victordorado.es/2024/12/19/bring-the-aws-cloud-to-your-desktop-with-localstack-and-drupal/">Bring the AWS Cloud to Your Desktop with LocalStack™, DDEV and Drupal</a> appeared first on <a href="https://www.victordorado.es">Víctor Dorado</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.victordorado.es/2024/12/19/bring-the-aws-cloud-to-your-desktop-with-localstack-and-drupal/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Improve your code quality from PHPStorm with PHP_CodeSniffer</title>
		<link>https://www.victordorado.es/2024/06/28/improve-your-code-quality-with-php-codesniffer-phpstan-eslint-stylelint-phpstorm-integration/</link>
		
		<dc:creator><![CDATA[vidorado]]></dc:creator>
		<pubDate>Fri, 28 Jun 2024 20:01:01 +0000</pubDate>
				<category><![CDATA[DDEV]]></category>
		<category><![CDATA[Drupal]]></category>
		<category><![CDATA[PHPStorm]]></category>
		<guid isPermaLink="false">https://www.victordorado.es/?p=2284</guid>

					<description><![CDATA[<p>Learn how to set up some quality tools with PHPStorm and DDEV: PHP_CodeSniffer, PHPStan, and others</p>
<p>The post <a href="https://www.victordorado.es/2024/06/28/improve-your-code-quality-with-php-codesniffer-phpstan-eslint-stylelint-phpstorm-integration/">Improve your code quality from PHPStorm with PHP_CodeSniffer</a> appeared first on <a href="https://www.victordorado.es">Víctor Dorado</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph"><strong>Following a consistent code style in a project is crucial for making it easier for developers to read each other&#8217;s code</strong>. When the code feels familiar, cognitive effort can be redirected to more important tasks.</p>



<p class="wp-block-paragraph">However, <strong>adhering to agreed-upon rules isn&#8217;t always easy.</strong> <strong>With many conventions to remember, we need tools to assist us</strong>. This is where our quality tools come in. They provide a powerful toolkit to ensure that our coding standards and style agreements are met. Furthermore, we can configure PHPStorm to use these tools, creating an integrated in-editor coding experience.</p>



<p class="wp-block-paragraph"><strong>In this article we will be working on a Drupal project, but the process is applicable to any project type</strong>, as long as we will be using standard npm/yarn and Composer package management to get the tools.</p>



<h2 class="wp-block-heading">Installing PHP_CodeSniffer</h2>



<p class="wp-block-paragraph">We will install it with Composer, as usual for many other PHP packages. We will also use a handy tool to ease the installation process.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="#ddev composer require --dev squizlabs/php_codesniffer dealerdirect/phpcodesniffer-composer-installer drupal/coder" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">ddev</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">composer</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">require</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">--</span><span style="color: #D8DEE9">dev</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">squizlabs</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9">php_codesniffer</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">dealerdirect</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9">phpcodesniffer</span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9">composer</span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9">installer</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">drupal</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9">coder</span></span></code></pre></div>



<p class="wp-block-paragraph">After that, as the documentation states (<a href="https://packagist.org/packages/dealerdirect/phpcodesniffer-composer-installer">https://packagist.org/packages/dealerdirect/phpcodesniffer-composer-installer</a>) we must ensure the following config is present in our composer.json:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="{
    &quot;config&quot;: {
        &quot;allow-plugins&quot;: {
            &quot;dealerdirect/phpcodesniffer-composer-installer&quot;: true
        }
    }
}" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">config</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">: </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">allow-plugins</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">dealerdirect/phpcodesniffer-composer-installer</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p class="wp-block-paragraph">If we are using Composer &lt; 2.2 it won&#8217;t and we will have to add it with the following command:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="# composer config allow-plugins.dealerdirect/phpcodesniffer-composer-installer true" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF"># </span><span style="color: #D8DEE9">composer</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">config</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">allow</span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9">plugins</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">dealerdirect</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9">phpcodesniffer</span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9">composer</span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9">installer</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true</span></span></code></pre></div>



<p class="wp-block-paragraph">After that, we will let the magic happen:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="# composer install" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF"># </span><span style="color: #D8DEE9">composer</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">install</span></span></code></pre></div>



<p class="wp-block-paragraph"><strong>Now, we can execute the phpcs command inside our DDEV web container</strong>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="# ddev exec phpcs --version
Registering sniffs in the PEAR standard... DONE (28 sniffs registered)
PHP_CodeSniffer version 3.10.1 (stable) by Squiz and PHPCSStandards" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF"># </span><span style="color: #D8DEE9">ddev</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">exec</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">phpcs</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">--</span><span style="color: #D8DEE9">version</span></span>
<span class="line"><span style="color: #D8DEE9">Registering</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">sniffs</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">in</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">the</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">PEAR</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">standard</span><span style="color: #81A1C1">...</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">DONE</span><span style="color: #D8DEE9FF"> (</span><span style="color: #B48EAD">28</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">sniffs</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">registered</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9">PHP_CodeSniffer</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">version</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">3.10</span><span style="color: #ECEFF4">.</span><span style="color: #B48EAD">1</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">stable</span><span style="color: #D8DEE9FF">) </span><span style="color: #D8DEE9">by</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">Squiz</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">and</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">PHPCSStandards</span></span></code></pre></div>



<p class="wp-block-paragraph">Note that PHP_CodeSniffer says something about a &#8220;standard&#8221;. <strong>PHP_CodeSniffer checks our code against one of various possible &#8220;standards&#8221;</strong>, and there are some of them bundled by default with it. The default standard that will be used is the &#8220;PEAR&#8221; one.</p>



<p class="wp-block-paragraph">But, for a Drupal project (a website, a theme, a contrib module&#8230; etc.), <strong>we must use a special crafted standard called &#8220;Drupal&#8221;</strong>, that has all the Drupal community-agreed coding standards. Thanks to our previously installed <code>dealerdirect/phpcodesniffer-composer-installer</code>, we can see that the standards &#8220;Drupal&#8221; and &#8220;DrupalPractice&#8221; are installed:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="# ddev exec phpcs -i
The installed coding standards are MySource, PEAR, PSR1, PSR2, PSR12, Squiz, Zend, Drupal, DrupalPractice, VariableAnalysis and SlevomatCodingStandard" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF"># </span><span style="color: #D8DEE9">ddev</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">exec</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">phpcs</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9">i</span></span>
<span class="line"><span style="color: #D8DEE9">The</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">installed</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">coding</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">standards</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">are</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">MySource</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">PEAR</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">PSR1</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">PSR2</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">PSR12</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">Squiz</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">Zend</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">Drupal</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">DrupalPractice</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">VariableAnalysis</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">and</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">SlevomatCodingStandard</span></span></code></pre></div>



<p class="wp-block-paragraph">(DrupalPractice is a more exhaustive standard that checks not only the coding style used, but also some best practices for Drupal development).</p>



<p class="wp-block-paragraph"><strong>In this article, we will focus in the use of PHPCS through the UI, that is what will make a more &#8220;realtime&#8221; development experience</strong>. Using phpcs from the command line is harder, and is often only used by cli scripts, like in CI environment QA tests, or s Git <a href="https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks">precommit hook</a> in a local development environment.</p>



<h2 class="wp-block-heading">Integrating PHP_CodeSniffer with PHPStorm</h2>



<p class="wp-block-paragraph">PHPStorm has friendly configuration dialogs for some popular quality tools, and one of them is PHPCS.</p>



<p class="wp-block-paragraph"><strong>Is highly recommended executing PHPCS inside our DDEV container</strong>, <strong>in order to keep consistency with our project&#8217;s PHP version and configuration</strong> instead of relying on our own machine config. To do so, first we must have a PHPStorm &#8220;DDEV&#8221; PHP interpreter (check my previous article <a href="https://www.victordorado.es/2024/03/18/phpunit-up-and-running-with-drupal-ddev-and-phpstorm/">PHPUnit up and running with Drupal, DDEV and PHPStorm</a> to know how to accomplish it).</p>



<p class="wp-block-paragraph">Then, go to Settings, under the &#8220;PHP &#8211; Quality Tools&#8221; section, and open the first accordion, setting its configuration and options as follows:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="993" height="312" src="https://www.victordorado.es/wp-content/uploads/2024/06/image-4.png" alt="" class="wp-image-2286" srcset="https://www.victordorado.es/wp-content/uploads/2024/06/image-4.png 993w, https://www.victordorado.es/wp-content/uploads/2024/06/image-4-300x94.png 300w, https://www.victordorado.es/wp-content/uploads/2024/06/image-4-768x241.png 768w" sizes="(max-width: 993px) 100vw, 993px" /></figure>



<p class="wp-block-paragraph"><strong>Notice that Drupal Core uses a different more relaxed standard</strong>. If you are going to develop for core you should set the coding standard to &#8220;Custom&#8221; and point to your project&#8217;s <code>&lt;drupal-root>/core/phpcs.xml.dist</code></p>



<p class="wp-block-paragraph">You can also configure some additional inspection settings in the &#8220;Editor &#8211; Inspections&#8221; menu:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="422" src="https://www.victordorado.es/wp-content/uploads/2024/06/image-5-1024x422.png" alt="" class="wp-image-2287" srcset="https://www.victordorado.es/wp-content/uploads/2024/06/image-5-1024x422.png 1024w, https://www.victordorado.es/wp-content/uploads/2024/06/image-5-300x124.png 300w, https://www.victordorado.es/wp-content/uploads/2024/06/image-5-768x316.png 768w, https://www.victordorado.es/wp-content/uploads/2024/06/image-5.png 1158w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Once configured, edit any file and <strong>PHPStorm will automatically execute PHPCS, showing the warnings and errors</strong> in that file.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="946" height="408" src="https://www.victordorado.es/wp-content/uploads/2024/06/image-6.png" alt="" class="wp-image-2288" srcset="https://www.victordorado.es/wp-content/uploads/2024/06/image-6.png 946w, https://www.victordorado.es/wp-content/uploads/2024/06/image-6-300x129.png 300w, https://www.victordorado.es/wp-content/uploads/2024/06/image-6-768x331.png 768w" sizes="(max-width: 946px) 100vw, 946px" /></figure>



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



<p class="wp-block-paragraph">Maintaining a consistent code style in our projects is made easier with these supportive tools. Including them in every developer&#8217;s toolkit is highly beneficial.</p>
<p>The post <a href="https://www.victordorado.es/2024/06/28/improve-your-code-quality-with-php-codesniffer-phpstan-eslint-stylelint-phpstorm-integration/">Improve your code quality from PHPStorm with PHP_CodeSniffer</a> appeared first on <a href="https://www.victordorado.es">Víctor Dorado</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>PHPUnit up and running with Drupal, DDEV and PHPStorm</title>
		<link>https://www.victordorado.es/2024/03/18/phpunit-up-and-running-with-drupal-ddev-and-phpstorm/</link>
		
		<dc:creator><![CDATA[vidorado]]></dc:creator>
		<pubDate>Mon, 18 Mar 2024 00:01:03 +0000</pubDate>
				<category><![CDATA[DDEV]]></category>
		<category><![CDATA[Drupal]]></category>
		<category><![CDATA[PHPStorm]]></category>
		<guid isPermaLink="false">https://www.victordorado.es/?p=2229</guid>

					<description><![CDATA[<p>Getting PHPStorm, DDEV, Drupal and PHPUnit to work together is not a trivial task. Read this guide to get up and running!</p>
<p>The post <a href="https://www.victordorado.es/2024/03/18/phpunit-up-and-running-with-drupal-ddev-and-phpstorm/">PHPUnit up and running with Drupal, DDEV and PHPStorm</a> appeared first on <a href="https://www.victordorado.es">Víctor Dorado</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">There are some articles out there about this topic: like the official <a href="https://ddev.readthedocs.io/en/latest/users/install/phpstorm/#enabling-phpunit" data-type="link" data-id="https://ddev.readthedocs.io/en/latest/users/install/phpstorm/#enabling-phpunit" target="_blank" rel="noreferrer noopener">DDEV docs PHPStorm &#8211; Enabling PHPUnit</a> or Matt Glaman&#8217;s <a href="https://mglaman.dev/blog/running-drupals-phpunit-test-suites-ddev" data-type="link" data-id="https://mglaman.dev/blog/running-drupals-phpunit-test-suites-ddev" target="_blank" rel="noreferrer noopener">Running Drupal&#8217;s PHPUnit test suites on DDEV</a>, <strong>but</strong> <strong>I haven&#8217;t found it so easy to figure it out. So, I&#8217;m writing this guide</strong>, although knowing that one day it will become obsolete, like it always have been with guides and new versions of things 🙂</p>



<h2 class="wp-block-heading">First step: enabling DDEV integration plugin for PHPStorm</h2>



<p class="wp-block-paragraph">DDEV has a PHPStorm plugin that eases a lot working with DDEV. <strong>One of the great things DDEV&#8217;s plugin does is creating a CLI interpreter</strong> that executes PHP code inside the web container. This will be the cornerstone of our setup.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="675" src="https://www.victordorado.es/wp-content/uploads/2024/03/image-7-1024x675.png" alt="" class="wp-image-2231" srcset="https://www.victordorado.es/wp-content/uploads/2024/03/image-7-1024x675.png 1024w, https://www.victordorado.es/wp-content/uploads/2024/03/image-7-300x198.png 300w, https://www.victordorado.es/wp-content/uploads/2024/03/image-7-768x507.png 768w, https://www.victordorado.es/wp-content/uploads/2024/03/image-7.png 1128w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h2 class="wp-block-heading">Setting DDEV as the project&#8217;s CLI interpreter and the Test framework interpreter</h2>



<p class="wp-block-paragraph">Under Settings -&gt; PHP section, we can now select the DDEV CLI interpreter:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="183" src="https://www.victordorado.es/wp-content/uploads/2024/03/image-9-1024x183.png" alt="" class="wp-image-2232" srcset="https://www.victordorado.es/wp-content/uploads/2024/03/image-9-1024x183.png 1024w, https://www.victordorado.es/wp-content/uploads/2024/03/image-9-300x54.png 300w, https://www.victordorado.es/wp-content/uploads/2024/03/image-9-768x137.png 768w, https://www.victordorado.es/wp-content/uploads/2024/03/image-9.png 1129w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">If you don&#8217;t see the interpreter, check that you have a Docker connection added in Build, Execution, Deployment -&gt; Docker:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="685" src="https://www.victordorado.es/wp-content/uploads/2024/03/image-10-1024x685.png" alt="" class="wp-image-2233" srcset="https://www.victordorado.es/wp-content/uploads/2024/03/image-10-1024x685.png 1024w, https://www.victordorado.es/wp-content/uploads/2024/03/image-10-300x201.png 300w, https://www.victordorado.es/wp-content/uploads/2024/03/image-10-768x514.png 768w, https://www.victordorado.es/wp-content/uploads/2024/03/image-10.png 1113w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">And check that &#8220;Use Compose V2&#8221; is enabled under the Tools subsection:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="685" src="https://www.victordorado.es/wp-content/uploads/2024/03/image-11-1024x685.png" alt="" class="wp-image-2234" srcset="https://www.victordorado.es/wp-content/uploads/2024/03/image-11-1024x685.png 1024w, https://www.victordorado.es/wp-content/uploads/2024/03/image-11-300x201.png 300w, https://www.victordorado.es/wp-content/uploads/2024/03/image-11-768x514.png 768w, https://www.victordorado.es/wp-content/uploads/2024/03/image-11.png 1113w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Now, we must go to PHP -&gt; Test Frameworks and add a new one that uses the DDEV CLI interpreter:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="546" src="https://www.victordorado.es/wp-content/uploads/2024/03/image-12-1024x546.png" alt="" class="wp-image-2235" srcset="https://www.victordorado.es/wp-content/uploads/2024/03/image-12-1024x546.png 1024w, https://www.victordorado.es/wp-content/uploads/2024/03/image-12-300x160.png 300w, https://www.victordorado.es/wp-content/uploads/2024/03/image-12-768x409.png 768w, https://www.victordorado.es/wp-content/uploads/2024/03/image-12.png 1394w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">We must also set a custom Default configuration file named phpunit.xml and located in our project&#8217;s root directory. DDEV&#8217;s CLI interpreter runs inside the web docker container, so we must set the path as it&#8217;s seen inside the container: <code>/var/www/html/phpunit.xml</code>. (this file doesn&#8217;t exist yet, we will create it later).</p>



<p class="wp-block-paragraph"><strong>Now, we have to tell PHPStorm to run this project&#8217;s tests with this new test framework</strong>. Go to Run -&gt; Edit configurations menu and clear any configuration you may find here.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="727" height="352" src="https://www.victordorado.es/wp-content/uploads/2024/03/image-14.png" alt="" class="wp-image-2237" style="object-fit:cover" srcset="https://www.victordorado.es/wp-content/uploads/2024/03/image-14.png 727w, https://www.victordorado.es/wp-content/uploads/2024/03/image-14-300x145.png 300w" sizes="(max-width: 727px) 100vw, 727px" /></figure>



<p class="wp-block-paragraph">Then, click on Edit configuration templates&#8230; -&gt; PHPUnit and set DDEV as the Command Line Interpreter:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1007" height="681" src="https://www.victordorado.es/wp-content/uploads/2024/03/image-13.png" alt="" class="wp-image-2236" srcset="https://www.victordorado.es/wp-content/uploads/2024/03/image-13.png 1007w, https://www.victordorado.es/wp-content/uploads/2024/03/image-13-300x203.png 300w, https://www.victordorado.es/wp-content/uploads/2024/03/image-13-768x519.png 768w" sizes="(max-width: 1007px) 100vw, 1007px" /></figure>



<h2 class="wp-block-heading">Creating project&#8217;s PHPUnit configuration file</h2>



<p class="wp-block-paragraph">Copy core&#8217;s PHPUnit configuration file <code>/web/core/phpunit.xml.dist</code> to <code>/phpunit.xml</code>, and edit it as follows:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;

&lt;phpunit xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
         bootstrap=&quot;web/core/tests/bootstrap.php&quot;
         colors=&quot;true&quot;
         beStrictAboutTestsThatDoNotTestAnything=&quot;true&quot;
         beStrictAboutOutputDuringTests=&quot;true&quot;
         beStrictAboutChangesToGlobalState=&quot;true&quot;
         failOnWarning=&quot;true&quot;
         printerClass=&quot;\Drupal\Tests\Listeners\HtmlOutputPrinter&quot;
         cacheResult=&quot;false&quot;
         xsi:noNamespaceSchemaLocation=&quot;https://schema.phpunit.de/9.3/phpunit.xsd&quot;&gt;
  &lt;php&gt;
    &lt;ini name=&quot;error_reporting&quot; value=&quot;32767&quot;/&gt; &lt;!-- 32767 = E_ALL. --&gt;
    &lt;ini name=&quot;memory_limit&quot; value=&quot;-1&quot;/&gt;
    &lt;env name=&quot;SIMPLETEST_BASE_URL&quot; value=&quot;http://my-project.ddev.site&quot;/&gt;
    &lt;env name=&quot;SIMPLETEST_DB&quot; value=&quot;mysql://db:db@db:3306/test&quot;/&gt;
    &lt;env name=&quot;BROWSERTEST_OUTPUT_DIRECTORY&quot; value=&quot;&quot;/&gt;
&lt;!--    &lt;env name=&quot;SYMFONY_DEPRECATIONS_HELPER&quot; value=&quot;weak&quot;/&gt;--&gt;
  &lt;/php&gt;

  &lt;testsuites&gt;
    &lt;testsuite name=&quot;unit&quot;&gt;
      &lt;directory&gt;web/modules/custom/*/tests/src/Unit/&lt;/directory&gt;
    &lt;/testsuite&gt;
    &lt;testsuite name=&quot;kernel&quot;&gt;
      &lt;directory&gt;web/modules/custom/*/tests/src/Kernel/&lt;/directory&gt;
    &lt;/testsuite&gt;
    &lt;testsuite name=&quot;functional&quot;&gt;
      &lt;directory&gt;web/modules/custom/*/tests/src/Functional/&lt;/directory&gt;
    &lt;/testsuite&gt;
    &lt;testsuite name=&quot;functional-javascript&quot;&gt;
      &lt;directory&gt;web/modules/custom/*/tests/src/FunctionalJavascript/&lt;/directory&gt;
    &lt;/testsuite&gt;
  &lt;/testsuites&gt;

  &lt;listeners&gt;
    &lt;listener class=&quot;\Drupal\Tests\Listeners\DrupalListener&quot;&gt;
    &lt;/listener&gt;
    &lt;!-- The Symfony deprecation listener has to come after the Drupal listener --&gt;
    &lt;listener class=&quot;Symfony\Bridge\PhpUnit\SymfonyTestsListener&quot;&gt;
    &lt;/listener&gt;
  &lt;/listeners&gt;

  &lt;coverage&gt;
    &lt;include&gt;
      &lt;directory suffix=&quot;.php&quot;&gt;web/modules/custom/*/src&lt;/directory&gt;
    &lt;/include&gt;
  &lt;/coverage&gt;

&lt;/phpunit&gt;" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">&lt;?</span><span style="color: #5E81AC">xml</span><span style="color: #8FBCBB"> version</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">1.0</span><span style="color: #ECEFF4">&quot;</span><span style="color: #8FBCBB"> encoding</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">UTF-8</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">?&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">&lt;phpunit</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">xmlns</span><span style="color: #ECEFF4">:</span><span style="color: #8FBCBB">xsi</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">http://www.w3.org/2001/XMLSchema-instance</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">         </span><span style="color: #8FBCBB">bootstrap</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">web/core/tests/bootstrap.php</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">         </span><span style="color: #8FBCBB">colors</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">true</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">         </span><span style="color: #8FBCBB">beStrictAboutTestsThatDoNotTestAnything</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">true</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">         </span><span style="color: #8FBCBB">beStrictAboutOutputDuringTests</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">true</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">         </span><span style="color: #8FBCBB">beStrictAboutChangesToGlobalState</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">true</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">         </span><span style="color: #8FBCBB">failOnWarning</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">true</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">         </span><span style="color: #8FBCBB">printerClass</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">\Drupal\Tests\Listeners\HtmlOutputPrinter</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">         </span><span style="color: #8FBCBB">cacheResult</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">false</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">         </span><span style="color: #8FBCBB">xsi</span><span style="color: #ECEFF4">:</span><span style="color: #8FBCBB">noNamespaceSchemaLocation</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">https://schema.phpunit.de/9.3/phpunit.xsd</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">&lt;php&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;ini</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">name</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">error_reporting</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">value</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">32767</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">/&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">&lt;!-- 32767 = E_ALL. --&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;ini</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">name</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">memory_limit</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">value</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">-1</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">/&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;env</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">name</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">SIMPLETEST_BASE_URL</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">value</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">http://my-project.ddev.site</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">/&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;env</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">name</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">SIMPLETEST_DB</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">value</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">mysql://db:db@db:3306/test</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">/&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;env</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">name</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">BROWSERTEST_OUTPUT_DIRECTORY</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">value</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;&quot;</span><span style="color: #81A1C1">/&gt;</span></span>
<span class="line"><span style="color: #616E88">&lt;!--    &lt;env name=&quot;SYMFONY_DEPRECATIONS_HELPER&quot; value=&quot;weak&quot;/&gt;--&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">&lt;/php&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">&lt;testsuites&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;testsuite</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">name</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">unit</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;directory&gt;</span><span style="color: #D8DEE9FF">web/modules/custom/*/tests/src/Unit/</span><span style="color: #81A1C1">&lt;/directory&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;/testsuite&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;testsuite</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">name</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">kernel</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;directory&gt;</span><span style="color: #D8DEE9FF">web/modules/custom/*/tests/src/Kernel/</span><span style="color: #81A1C1">&lt;/directory&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;/testsuite&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;testsuite</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">name</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">functional</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;directory&gt;</span><span style="color: #D8DEE9FF">web/modules/custom/*/tests/src/Functional/</span><span style="color: #81A1C1">&lt;/directory&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;/testsuite&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;testsuite</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">name</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">functional-javascript</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;directory&gt;</span><span style="color: #D8DEE9FF">web/modules/custom/*/tests/src/FunctionalJavascript/</span><span style="color: #81A1C1">&lt;/directory&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;/testsuite&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">&lt;/testsuites&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">&lt;listeners&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;listener</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">class</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">\Drupal\Tests\Listeners\DrupalListener</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;/listener&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #616E88">&lt;!-- The Symfony deprecation listener has to come after the Drupal listener --&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;listener</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">class</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Symfony\Bridge\PhpUnit\SymfonyTestsListener</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;/listener&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">&lt;/listeners&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">&lt;coverage&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;include&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;directory</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">suffix</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">.php</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span><span style="color: #D8DEE9FF">web/modules/custom/*/src</span><span style="color: #81A1C1">&lt;/directory&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;/include&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">&lt;/coverage&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">&lt;/phpunit&gt;</span></span></code></pre></div>



<p class="wp-block-paragraph"><strong>These are all the customizations that have been made:</strong></p>



<p class="wp-block-paragraph">1. Adjust the boostrap path to core&#8217;s bootstrap</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="bootstrap=&quot;web/core/tests/bootstrap.php&quot;" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF">bootstrap=&quot;web/core/tests/bootstrap.php&quot;</span></span></code></pre></div>



<p class="wp-block-paragraph">2. Set site URL and database connection details (for Kernel, Functional&#8230; etc. tests). Note that PHPUnit will be executed inside the web container, so we must set &#8220;db&#8221; and port 3306 as our database host, as it is seen from the web container. We also set the testing database name to &#8220;test&#8221;, that is the convention.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="&lt;env name=&quot;SIMPLETEST_BASE_URL&quot; value=&quot;https://my-project.ddev.site&quot;/&gt;
&lt;env name=&quot;SIMPLETEST_DB&quot; value=&quot;mysql://db:db@db:3306/test&quot;/&gt;" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">&lt;env</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">name</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">SIMPLETEST_BASE_URL</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">value</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">https://my-project.ddev.site</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">/&gt;</span></span>
<span class="line"><span style="color: #81A1C1">&lt;env</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">name</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">SIMPLETEST_DB</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">value</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">mysql://db:db@db:3306/test</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">/&gt;</span></span></code></pre></div>



<p class="wp-block-paragraph">3. Finally, we adjust all test suites to only execute tests from our custom modules by default, if we execute phpunit without file parameters nor filters.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="&lt;testsuite name=&quot;unit&quot;&gt;
  &lt;directory&gt;web/modules/custom/*/tests/src/Unit/&lt;/directory&gt;
&lt;/testsuite&gt;
&lt;testsuite name=&quot;kernel&quot;&gt;
  &lt;directory&gt;web/modules/custom/*/tests/src/Kernel/&lt;/directory&gt;
&lt;/testsuite&gt;
..." style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">&lt;testsuite</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">name</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">unit</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">&lt;directory&gt;</span><span style="color: #D8DEE9FF">web/modules/custom/*/tests/src/Unit/</span><span style="color: #81A1C1">&lt;/directory&gt;</span></span>
<span class="line"><span style="color: #81A1C1">&lt;/testsuite&gt;</span></span>
<span class="line"><span style="color: #81A1C1">&lt;testsuite</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">name</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">kernel</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">&lt;directory&gt;</span><span style="color: #D8DEE9FF">web/modules/custom/*/tests/src/Kernel/</span><span style="color: #81A1C1">&lt;/directory&gt;</span></span>
<span class="line"><span style="color: #81A1C1">&lt;/testsuite&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">...</span></span></code></pre></div>



<h2 class="wp-block-heading">Installing PHPUnit</h2>



<p class="wp-block-paragraph">As simple as installing these packages with Composer</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="# ddev composer require --dev \
  phpunit/phpunit \
  phpspec/prophecy-phpunit \
  symfony/phpunit-bridge \
  dmore/chrome-mink-driver \
  behat/mink-browserkit-driver \
  behat/mink-selenium2-driver \
  mikey179/vfsstream" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF"># ddev composer require --dev \</span></span>
<span class="line"><span style="color: #D8DEE9FF">  phpunit/phpunit \</span></span>
<span class="line"><span style="color: #D8DEE9FF">  phpspec/prophecy-phpunit \</span></span>
<span class="line"><span style="color: #D8DEE9FF">  symfony/phpunit-bridge \</span></span>
<span class="line"><span style="color: #D8DEE9FF">  dmore/chrome-mink-driver \</span></span>
<span class="line"><span style="color: #D8DEE9FF">  behat/mink-browserkit-driver \</span></span>
<span class="line"><span style="color: #D8DEE9FF">  behat/mink-selenium2-driver \</span></span>
<span class="line"><span style="color: #D8DEE9FF">  mikey179/vfsstream</span></span></code></pre></div>



<ul class="wp-block-list">
<li><strong>phpunit/phpunit</strong> &#8211; The PHPUnit core package.</li>



<li><strong>phpspec/prophecy-phpunit</strong> &#8211; The Prophecy mocking system. In a lot of tests it is used instead of PHPUnit Mock Object Framework, the PHPUnit standard.</li>



<li><strong>symfony/phpunit-bridge</strong> &#8211; Bridges the gap between Symfony and PHPUnit.</li>



<li><strong>dmore/chrome-mink-driver</strong> &#8211; Required for some Kernel nad Functional tests.</li>



<li><strong>behat/mink-browserkit-driver</strong> &#8211; Required for some Functional tests.</li>



<li><strong>mikey179/vfsstream</strong> &#8211; Some tests make use of this virtual filesystem mocker.</li>
</ul>



<h2 class="wp-block-heading">Configuring DDEV Selenium Standalone Chrome</h2>



<p class="wp-block-paragraph">This config is necessary to execute Javascript and FunctionalJavascript Drupal tests. PHPUnit needs a headless Chromium browser to control and to send commands to. That&#8217;s what this DDEV addon provides.</p>



<p class="wp-block-paragraph">Follow the instructions in <a href="https://github.com/ddev/ddev-selenium-standalone-chrome">https://github.com/ddev/ddev-selenium-standalone-chrome</a>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="# ddev get ddev/ddev-selenium-standalone-chrome" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF"># </span><span style="color: #D8DEE9">ddev</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">get</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ddev</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9">ddev</span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9">selenium</span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9">standalone</span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9">chrome</span></span></code></pre></div>



<p class="wp-block-paragraph">Then, update your <code>.ddev/config.selenium-standalone-chrome.yaml</code> and set the local domain for your DDEV project, making sure you set it as <code>http://</code> and not <code>https://</code> (I got an &#8220;invalid cookie domain&#8221; error when I did).</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="#ddev-generated  &lt;- REMOVE THIS LINE

...

- SIMPLETEST_BASE_URL=http://my-project.ddev.site

...

- DRUPAL_TEST_BASE_URL=http://my-project.ddev.site

...

- DTT_BASE_URL=http://my-project.ddev.site" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">ddev</span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9">generated</span><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">&lt;-</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">REMOVE</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">THIS</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">LINE</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">...</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">SIMPLETEST_BASE_URL</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF">http</span><span style="color: #ECEFF4">:</span><span style="color: #616E88">//my-project.ddev.site</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">...</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">DRUPAL_TEST_BASE_URL</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF">http</span><span style="color: #ECEFF4">:</span><span style="color: #616E88">//my-project.ddev.site</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">...</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">DTT_BASE_URL</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF">http</span><span style="color: #ECEFF4">:</span><span style="color: #616E88">//my-project.ddev.site</span></span></code></pre></div>



<p class="wp-block-paragraph">Then, restart DDEV:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="# ddev restart" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF"># </span><span style="color: #D8DEE9">ddev</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">restart</span></span></code></pre></div>



<h2 class="wp-block-heading">Executing tests from the command line</h2>



<p class="wp-block-paragraph">If we want being able to execute our tests easily from outside the web container, we must create the file <code>/.ddev/commands/web/phpunit</code> with this content:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="#!/bin/bash

phpunit &quot;$@&quot;" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88">#!/bin/bash</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">phpunit</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9">$@</span><span style="color: #ECEFF4">&quot;</span></span></code></pre></div>



<p class="wp-block-paragraph">And now, outside the web container, we can do:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="# ddev phpunit
# ddev phpunit --testsuite &lt;name&gt;" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF"># ddev phpunit</span></span>
<span class="line"><span style="color: #D8DEE9FF"># ddev phpunit --testsuite </span><span style="color: #81A1C1">&lt;name&gt;</span></span></code></pre></div>



<h2 class="wp-block-heading">Executing tests from PHPStorm</h2>



<p class="wp-block-paragraph">From PHPStorm&#8217;s interface, <strong>we can go to any test case class and use the GUI to run its tests</strong>, either all at once or one by one, simply clicking on the run icons next to the class and test method definitions:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="806" height="269" src="https://www.victordorado.es/wp-content/uploads/2024/03/image-16.png" alt="" class="wp-image-2241" srcset="https://www.victordorado.es/wp-content/uploads/2024/03/image-16.png 806w, https://www.victordorado.es/wp-content/uploads/2024/03/image-16-300x100.png 300w, https://www.victordorado.es/wp-content/uploads/2024/03/image-16-768x256.png 768w" sizes="(max-width: 806px) 100vw, 806px" /></figure>



<p class="wp-block-paragraph"><strong>We can even right click on any directory in the project&#8217;s explorer and click on &#8220;Run (tests PHPUnit)&#8221; and all tests in that directory will be executed.</strong> This is very convenient when we want to debug only a test method or execute only one type of tests inside a module, for example.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="162" src="https://www.victordorado.es/wp-content/uploads/2024/03/image-15-1024x162.png" alt="" class="wp-image-2239" srcset="https://www.victordorado.es/wp-content/uploads/2024/03/image-15-1024x162.png 1024w, https://www.victordorado.es/wp-content/uploads/2024/03/image-15-300x47.png 300w, https://www.victordorado.es/wp-content/uploads/2024/03/image-15-768x121.png 768w, https://www.victordorado.es/wp-content/uploads/2024/03/image-15-1536x243.png 1536w, https://www.victordorado.es/wp-content/uploads/2024/03/image-15.png 1729w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph"></p>



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



<p class="wp-block-paragraph"><strong>Getting PHPStorm, DDEV, Drupal and PHPUnit to work together is not a trivial task</strong>. Although DDEV&#8217;s PHPStorm plugin eases a bit the task, we have yet to wire all the things correctly.</p>
<p>The post <a href="https://www.victordorado.es/2024/03/18/phpunit-up-and-running-with-drupal-ddev-and-phpstorm/">PHPUnit up and running with Drupal, DDEV and PHPStorm</a> appeared first on <a href="https://www.victordorado.es">Víctor Dorado</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Debug your Drupal code with PHPStorm, DDEV and Xdebug</title>
		<link>https://www.victordorado.es/2024/03/17/debug-your-drupal-code-with-phpstorm-ddev-and-xdebug/</link>
		
		<dc:creator><![CDATA[vidorado]]></dc:creator>
		<pubDate>Sun, 17 Mar 2024 17:37:16 +0000</pubDate>
				<category><![CDATA[DDEV]]></category>
		<category><![CDATA[Drupal]]></category>
		<category><![CDATA[PHPStorm]]></category>
		<guid isPermaLink="false">https://www.victordorado.es/?p=2186</guid>

					<description><![CDATA[<p>Debugging goes next level when it's done with a tool like Xdebug and through a debugging GUI like PHPStorm has.</p>
<p>The post <a href="https://www.victordorado.es/2024/03/17/debug-your-drupal-code-with-phpstorm-ddev-and-xdebug/">Debug your Drupal code with PHPStorm, DDEV and Xdebug</a> appeared first on <a href="https://www.victordorado.es">Víctor Dorado</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph"><strong>How would have been our lives without debuggers?</strong> We would have been spreading <code>print_r</code>, <code>echo</code> and the like everywhere. Our world got better with tools like Krumo or (in the case of Drupal) with <code>dpm()</code>, but nothing like a real [IDE + debugger] couple like [PHPStorm + Xdebug].</p>



<p class="wp-block-paragraph">If you also add DDEV and its docker containers to the mix, it&#8217;s not easy to get all pieces working, without a proper recipe. This article aims to be that recipe.</p>



<h2 class="wp-block-heading">Preparing DDEV&#8217;s web container</h2>



<p class="wp-block-paragraph">DDEV automatically adds two environment variables to its <code>.ddev-docker-compose-base.yaml</code> file:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="PHP_IDE_CONFIG=serverName=${DDEV_SITENAME}.${DDEV_TLD}
DRUSH_ALLOW_XDEBUG=1" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">PHP_IDE_CONFIG</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9">serverName</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9">$</span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9">DDEV_SITENAME</span><span style="color: #ECEFF4">}.</span><span style="color: #D8DEE9">$</span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9">DDEV_TLD</span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9">DRUSH_ALLOW_XDEBUG</span><span style="color: #81A1C1">=</span><span style="color: #B48EAD">1</span></span></code></pre></div>



<p class="wp-block-paragraph"><strong>Mind the serverName because we will need it later</strong>, when configuring a server in PHPStorm.</p>



<p class="wp-block-paragraph">Now, we must config the Xdebug PHP extension. Create a new directory <code>.ddev/php/</code> and create a file called <code>xdebug.ini</code> inside it, with this content.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="xdebug.client_port = 9000
xdebug.idekey = PHPSTORM
xdebug.mode = develop,debug
xdebug.start_with_request = yes" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">xdebug</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">client_port</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">9000</span></span>
<span class="line"><span style="color: #D8DEE9">xdebug</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">idekey</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">PHPSTORM</span></span>
<span class="line"><span style="color: #D8DEE9">xdebug</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">mode</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">develop</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9">debug</span></span>
<span class="line"><span style="color: #D8DEE9">xdebug</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">start_with_request</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">yes</span></span></code></pre></div>



<p class="wp-block-paragraph">I like to set <code>xdebug.start_with_request=yes</code> and turn on and off the web contaniner&#8217;s xdebug PHP extension with <code>ddev xdebug on</code> and <code>ddev xdebug off</code>. You could set <code>xdebug.start_with_request=no</code> and play with a browser switch like <a href="https://chromewebstore.google.com/detail/xdebug-helper/eadndfjplgieldjbigjakmdgkmoaaaoc" data-type="link" data-id="https://chromewebstore.google.com/detail/xdebug-helper/eadndfjplgieldjbigjakmdgkmoaaaoc" target="_blank" rel="noreferrer noopener">Xdebug Helper</a>, but this won&#8217;t have effect on drush commands.</p>



<p class="wp-block-paragraph">After that, we have to restart DDEV&#8217;s web container:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="# ddev restart" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF"># </span><span style="color: #D8DEE9">ddev</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">restart</span></span></code></pre></div>



<h2 class="wp-block-heading">Configuring PHPStorm</h2>



<p class="wp-block-paragraph">Now, we must create a new PHP server under Settings -&gt; PHP -&gt; Servers. Recall the server name we saw in DDEV&#8217;s config: it was <code>${DDEV_SITENAME}.${DDEV_TLD}</code>, that translates to <code>my-project.ddev.site</code> if your DDEV project is named <code>my-project</code>.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="740" src="https://www.victordorado.es/wp-content/uploads/2024/03/image-1024x740.png" alt="" class="wp-image-2187" srcset="https://www.victordorado.es/wp-content/uploads/2024/03/image-1024x740.png 1024w, https://www.victordorado.es/wp-content/uploads/2024/03/image-300x217.png 300w, https://www.victordorado.es/wp-content/uploads/2024/03/image-768x555.png 768w, https://www.victordorado.es/wp-content/uploads/2024/03/image.png 1037w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">We need to set path mappings for the webserver docroot directory. A path mapping &#8220;maps&#8221; a real directory path in your filesystem with a directory inside the DDEV docker web container. In our case, <strong>a DDEV drupal project has its web server&#8217;s document root in <code>/var/www/html/web</code>, so we have to set this mapping</strong>:</p>



<ul class="wp-block-list">
<li><code>/path/to/your/project<strong>/web</strong> -&gt; /var/www/html<strong>/web</strong></code></li>
</ul>



<p class="wp-block-paragraph"><strong>However, I recommend also adding this mapping </strong>to be able to debug vendor code.</p>



<ul class="wp-block-list">
<li><code>/path/to/your/project<strong>/vendor</strong> -&gt; /var/www/html<strong>/vendor</strong></code></li>
</ul>



<p class="wp-block-paragraph">With these mappings, PHPStorm now knows how to map between the same file as it&#8217;s seen inside/outside DDEV&#8217;s web container.</p>



<p class="wp-block-paragraph">After that, to be sure everything is ok,<strong> I recommend checking and configuring the Xdebug installation</strong> under PHP -&gt; Debug:</p>



<p class="wp-block-paragraph">First, click on the &#8220;Validate&#8221; link:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="916" height="279" src="https://www.victordorado.es/wp-content/uploads/2024/03/image-1.png" alt="" class="wp-image-2188" srcset="https://www.victordorado.es/wp-content/uploads/2024/03/image-1.png 916w, https://www.victordorado.es/wp-content/uploads/2024/03/image-1-300x91.png 300w, https://www.victordorado.es/wp-content/uploads/2024/03/image-1-768x234.png 768w" sizes="(max-width: 916px) 100vw, 916px" /></figure>



<p class="wp-block-paragraph">In the next screen configure the docroot directory as it&#8217;s seen by PHPStorm (your real machine path to the web directory inside your project directory) and the URL where the project&#8217;s docroot will be accesible. Then, click on &#8220;Validate&#8221;.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="861" height="690" src="https://www.victordorado.es/wp-content/uploads/2024/03/image-2.png" alt="" class="wp-image-2189" srcset="https://www.victordorado.es/wp-content/uploads/2024/03/image-2.png 861w, https://www.victordorado.es/wp-content/uploads/2024/03/image-2-300x240.png 300w, https://www.victordorado.es/wp-content/uploads/2024/03/image-2-768x615.png 768w" sizes="(max-width: 861px) 100vw, 861px" /></figure>



<p class="wp-block-paragraph"><strong>I strongly recommend unchecking these two &#8220;Force break&#8221; checkboxes</strong>, because we don&#8217;t want the execution to be paused if we don&#8217;t set or don&#8217;t reach a breakpoint. I remember myself waiting a drush command to finish unaware that the execution was paused, because of this.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="908" height="744" src="https://www.victordorado.es/wp-content/uploads/2024/03/image-6.png" alt="" class="wp-image-2193" srcset="https://www.victordorado.es/wp-content/uploads/2024/03/image-6.png 908w, https://www.victordorado.es/wp-content/uploads/2024/03/image-6-300x246.png 300w, https://www.victordorado.es/wp-content/uploads/2024/03/image-6-768x629.png 768w" sizes="(max-width: 908px) 100vw, 908px" /></figure>



<p class="wp-block-paragraph">So far, we have configured DDEV and PHPStorm to &#8220;connect&#8221; between them and our next step will be to set a breakpoint. For example, we can break on the <code>handle()</code> call that <code>Drupal\Core\DrupalKernel</code> class does on every page load.</p>



<p class="wp-block-paragraph">Click next to the line number and a red dot will appear. This is our breakpoint. The PHP execution will stop here until we let it continue.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="437" src="https://www.victordorado.es/wp-content/uploads/2024/03/image-4-1024x437.png" alt="" class="wp-image-2191" srcset="https://www.victordorado.es/wp-content/uploads/2024/03/image-4-1024x437.png 1024w, https://www.victordorado.es/wp-content/uploads/2024/03/image-4-300x128.png 300w, https://www.victordorado.es/wp-content/uploads/2024/03/image-4-768x328.png 768w, https://www.victordorado.es/wp-content/uploads/2024/03/image-4.png 1404w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph"><strong>Remember to enable xdebug PHP extension on DDEV&#8217;s </strong>web container with</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="# ddev xdebug on" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF"># </span><span style="color: #D8DEE9">ddev</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">xdebug</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">on</span></span></code></pre></div>



<p class="wp-block-paragraph"><strong>And to tell PHPStorm to listen</strong> to incoming debug connections with the <img decoding="async" style="width:21px;height:auto;" src="https://www.victordorado.es/wp-content/uploads/2024/03/image-8.png" alt="Listen icon"> icon.</p>



<p class="wp-block-paragraph"><strong>Now, reload any page</strong> in your browser and you will view a system notification letting you know that PHPStorm requires your attention. Go for it and you will see the breakpoint&#8217;s line highlighted and <strong>a panel where you can explore all the variables in it&#8217;s scope.</strong></p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="470" src="https://www.victordorado.es/wp-content/uploads/2024/03/image-5-1024x470.png" alt="" class="wp-image-2192" srcset="https://www.victordorado.es/wp-content/uploads/2024/03/image-5-1024x470.png 1024w, https://www.victordorado.es/wp-content/uploads/2024/03/image-5-300x138.png 300w, https://www.victordorado.es/wp-content/uploads/2024/03/image-5-768x352.png 768w, https://www.victordorado.es/wp-content/uploads/2024/03/image-5.png 1426w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph"><strong>You can browse the variables panel and deep dive into the tree structures. This is VERY useful</strong>, specially when debugging a framework like Drupal that has many nesting levels on its variables.</p>



<p class="wp-block-paragraph"><strong>You can also select any piece of code, right click on it and click on &#8220;Evaluate expression&#8221;</strong> and you could test it inside that paused execution state. You can even write directly your own expressions.</p>



<p class="wp-block-paragraph">You can continue the execution step-by-step (step over, F8), even traveling inside method calls (step into, F7), or continue the execution until the next breakpoint (or the same breakpoint if it&#8217;s reached again)</p>



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



<p class="wp-block-paragraph">Debugging goes next level when it&#8217;s done with a tool like Xdebug and through a debugging GUI like PHPStorm has.</p>
<p>The post <a href="https://www.victordorado.es/2024/03/17/debug-your-drupal-code-with-phpstorm-ddev-and-xdebug/">Debug your Drupal code with PHPStorm, DDEV and Xdebug</a> appeared first on <a href="https://www.victordorado.es">Víctor Dorado</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
