{"@attributes":{"version":"2.0"},"channel":{"title":"meh.dev","description":{},"link":"https:\/\/meh.dev\/","pubDate":"Wed, 25 Mar 2026 05:47:37 +0000","lastBuildDate":"Wed, 25 Mar 2026 05:47:37 +0000","generator":"Jekyll v4.4.1","item":[{"title":"Rendering React via a Symfony Twig Extension","description":"<p>Looking for an easier way to render individual React components from Twig? Here\u2019s what I came up with on a recent project using a Twig Extension.<\/p>\n\n<p>The project &amp; samples were written using Symfony 6 with Encore and Webpack.<\/p>\n\n<h1 id=\"configure-an-entrypoint-in-webpack--encore\">Configure an Entrypoint in Webpack &amp; Encore<\/h1>\n<p>The entry script must be loaded on any page you want to render a component. You can create a single bundle and reuse it across many components and pages, or create multiple bundles.<\/p>\n\n<p>In my application and this example I use a shared <code>components<\/code> bundle which can be accessed on any page.<\/p>\n\n<p>Add an entrypoint to your <code>webpack.config.js<\/code>.<\/p>\n<pre><code class=\"language-javascript\">.addEntry('components', '.\/assets\/components.tsx')\n<\/code><\/pre>\n\n<p>Load the bundle on a page (or entire layout). This uses Encore inside a Twig file.<\/p>\n\n<figure class=\"highlight\"><pre><code class=\"language-php\" data-lang=\"php\">{{ encore_entry_script_tags(&#39;components&#39;) }}<\/code><\/pre><\/figure>\n\n<h1 id=\"create-componentstsx\">Create components.tsx<\/h1>\n<p>Create the components.tsx and add each component you want to load to the <code>componentMap<\/code> variable.<\/p>\n\n<pre><code class=\"language-javascript\">import React from 'react';\nimport ReactDOM from 'react-dom';\n\nimport EmbedModal from \".\/components\/EmbedModal\";\n\nconst componentMap = {\n    'EmbedModal': EmbedModal,\n};\n\nfunction getComponentByName(componentName: string) {\n    const Component = componentMap[componentName];\n    if (Component) {\n        return Component;\n    } else {\n        console.error(`Unknown component: ${componentName}`);\n    }\n}\n\ndocument.querySelectorAll('.react-container').forEach(container =&gt; {\n    \/\/ @ts-ignore\n    const componentName = container.dataset.component;\n    \/\/ @ts-ignore\n    const props = JSON.parse(container.dataset.props);\n\n    const Component = getComponentByName(componentName);\n    ReactDOM.render(&lt;React.StrictMode&gt;&lt;Component {...props} \/&gt;&lt;\/React.StrictMode&gt;, container);\n});\n<\/code><\/pre>\n\n<p>This example is fairly naive, you could make the componentMap more magical if you\u2019d like, but for a small number of components it\u2019s easy to manage them manually.<\/p>\n\n<h1 id=\"create-the-twigextension\">Create the TwigExtension<\/h1>\n\n<pre><code class=\"language-php\">&lt;?php\n\nnamespace App\\Twig;\n\nuse Twig\\Extension\\AbstractExtension;\nuse Twig\\TwigFunction;\n\nclass TwigExtension extends AbstractExtension\n{\n    public function getFunctions()\n    {\n        return [\n            new TwigFunction('renderReactComponent', [$this, 'renderReactComponent'], ['is_safe' =&gt; ['html']]),\n        ];\n    }\n\n    public function renderReactComponent($componentName, $props = null): string\n    {\n        if (is_null($props)) {\n            $props = [];\n        }\n        $prop_str = json_encode($props);\n        $prop_str = htmlspecialchars($prop_str, ENT_QUOTES, 'UTF-8');\n\n        return &lt;&lt;&lt;HTML\n                &lt;div class=\"react-container inline\" data-component=\"{$componentName}\" data-props=\"{$prop_str}\"&gt;&lt;\/div&gt;\n            HTML;\n    }\n}\n\n<\/code><\/pre>\n\n<p>Make sure your extension is loaded by Symfony, this might happen automatically or you may have to add the service manually.<\/p>\n\n<h1 id=\"render-a-component\">Render a Component<\/h1>\n<p>Now you\u2019re ready to render! Call the Twig extension anywhere in your twig files where you want a react component to appear.<\/p>\n\n<figure class=\"highlight\"><pre><code class=\"language-php\" data-lang=\"php\">{{ renderReactComponent(&#39;EmbedModal&#39;, {\n        &#39;name&#39;: event.name,\n        &#39;embed_url&#39;: event.getEmbedUrl(),\n}) }}<\/code><\/pre><\/figure>\n","pubDate":"Mon, 01 Apr 2024 02:16:40 +0000","link":"https:\/\/meh.dev\/render-react-component-twig","guid":"https:\/\/meh.dev\/render-react-component-twig","category":["PHP","Symfony","Twig","React"]},{"title":"Development with Docker, Apache, PHP, and SSL","description":"<p>This post goes through a sample project I built that shows this off using docker, apache, php, and xdebug for local development.<\/p>\n\n<p>Want to skip straight to the <a href=\"https:\/\/github.com\/jspaetzel\/docker-php\">complete sample project? Click here!<\/a><\/p>\n\n<p><strong>Project Overview<\/strong><\/p>\n<ul>\n  <li>docker\/certificates\/              \u2013 gitignored folder for holding your dev certs<\/li>\n  <li>docker\/web\/Dockerfile             \u2013 web container<\/li>\n  <li>docker\/web\/configs\/apache\/        \u2013 apache configs for example.test site<\/li>\n  <li>docker\/web\/configs\/php\/           - php configs (for xdebug with docker)<\/li>\n  <li>public\/                           \u2013 your application index<\/li>\n<\/ul>\n\n<h2 id=\"configuring-php81-apache\">Configuring php:8.1-apache<\/h2>\n<p>The default php:8.1-apache docker image isn\u2019t ready for development out of the box. Here\u2019s some of the changes I recommend making.<\/p>\n\n<p><strong>Installing PHP Extensions<\/strong><\/p>\n<pre><code class=\"language-Dockerfile\">COPY --from=mlocati\/php-extension-installer \/usr\/bin\/install-php-extensions \/usr\/local\/bin\/\nRUN set -eux; \\\n    install-php-extensions \\\n        xdebug \\\n    ;\n<\/code><\/pre>\n\n<p><strong>Customizing PHP<\/strong><\/p>\n<pre><code class=\"language-Dockerfile\">COPY docker\/web\/configs\/php\/*.ini $PHP_INI_DIR\/conf.d\/\n<\/code><\/pre>\n<p>This example only includes a xdebug.ini config, but you can add additional ini files here to make other changes.<\/p>\n\n<p><strong>Configure Apache sites and enabling mods<\/strong><\/p>\n<pre><code class=\"language-Dockerfile\">COPY .\/docker\/web\/configs\/apache\/sites-enabled\/* \/etc\/apache2\/sites-enabled\/\nRUN a2enmod ssl \\\n    &amp;&amp; a2enmod allowmethods \\\n    &amp;&amp; a2enmod headers \\\n    &amp;&amp; a2enmod mime \\\n    &amp;&amp; a2enmod rewrite\n<\/code><\/pre>\n\n<p>That\u2019s the bare minimum, here\u2019s a few more things you might want to do\n<strong>Install Composer<\/strong><\/p>\n<pre><code class=\"language-Dockerfile\">COPY --from=composer\/composer:2-bin \/composer \/usr\/bin\/composer\n<\/code><\/pre>\n\n<p><strong>Change the document root<\/strong><\/p>\n<pre><code class=\"language-Dockerfile\">ENV APACHE_DOCUMENT_ROOT \/var\/www\/html\/\nRUN sed -ri -e 's!\/var\/www\/html!${APACHE_DOCUMENT_ROOT}!g' \/etc\/apache2\/sites-available\/*.conf\nRUN sed -ri -e 's!\/var\/www\/!${APACHE_DOCUMENT_ROOT}!g' \/etc\/apache2\/apache2.conf \/etc\/apache2\/conf-available\/*.conf\n<\/code><\/pre>\n\n<p>View the <a href=\"https:\/\/github.com\/jspaetzel\/docker-php\/blob\/main\/docker\/web\/Dockerfile\">complete Dockerfile here<\/a>.<\/p>\n\n<h2 id=\"configuring-ssl\">Configuring SSL<\/h2>\n\n<p>You\u2019ll need some self-signed certificates in order to configure SSL. I\u2019ve used <a href=\"https:\/\/mkcert.dev\/\">mkcert<\/a> to create and trust them on my localhost. Before you proceed, <a href=\"https:\/\/github.com\/FiloSottile\/mkcert#installation\">install mkcert<\/a>.<\/p>\n<pre><code class=\"language-bash\">mkcert -install\nmkcert example.test\nmv example.*.pem docker\/certificates\/\n<\/code><\/pre>\n\n<h2 id=\"using-xdebug\">Using xDebug<\/h2>\n<p>With this configuration xDebug should be ready to go. The only thing I needed to do in PHPStorm is enable listening for connections and configure the path mapping.<\/p>\n\n<p><img src=\".\/assets\/phpstorm\/xdebug-example.test.png\" alt=\"example.test\" title=\"XDebug Config for example.test\" \/><\/p>\n\n<h2 id=\"conclusion\">Conclusion<\/h2>\n\n<p>There\u2019s only a few more things left to do.<\/p>\n\n<p><strong>Add an entry for the site to your hosts file<\/strong><\/p>\n<pre><code>127.0.0.1 example.test\n<\/code><\/pre>\n\n<p><strong>Start the containers<\/strong><\/p>\n<pre><code>docker compose up\n<\/code><\/pre>\n\n<p>You should now be able to access and debug the <code>public\/index.php<\/code> file by going to <a href=\"https:\/\/example.test\/\">https:\/\/example.test\/<\/a> in any browser on your host.<\/p>\n","pubDate":"Fri, 16 Dec 2022 01:32:50 +0000","link":"https:\/\/meh.dev\/docker-php-ssl","guid":"https:\/\/meh.dev\/docker-php-ssl","category":["Docker","PHP","Apache"]},{"title":"How to use a fork of a Composer Package","description":"<h2 id=\"1-fork-the-git-repository-on-github\">1. Fork the Git Repository on Github<\/h2>\n\n<h2 id=\"2-make-changes-in-a-branch\">2. Make changes in a branch<\/h2>\n\n<p>For this example I\u2019ll use the branch will be named <code>mybranch<\/code><\/p>\n\n<h2 id=\"3-add-repositories-entry-in-composerjson\">3. Add repositories entry in composer.json<\/h2>\n\n<pre><code class=\"language-bash\">\"repositories\": [\n    {\n        \"type\": \"vcs\",\n        \"url\": \"https:\/\/github.com\/yourgithubuser\/packagename\",\n        \"no-api\": true\n    }\n],\n<\/code><\/pre>\n\n<blockquote>\n  <p>\u26a0\ufe0f If you get an error like \u201cGitHub API limit (0 calls\/hr) is exhausted, could not fetch\u201d? make sure you have the <code>\"no-api\": true<\/code> configuration specified.<\/p>\n<\/blockquote>\n\n<h2 id=\"4-require-custom-version-in-composer\">4. Require custom version in Composer<\/h2>\n\n<p>Require the package with<\/p>\n<pre><code class=\"language-bash\">composer require vendor\/packagename:dev-mybranch\n<\/code><\/pre>\n\n<blockquote>\n  <p>\u2139 To avoid conflicts with other packages, you can alias your branch as a specific version\n<code>composer require \"vendor\/packagename:dev-mybranch as 1.0.0\"<\/code><\/p>\n<\/blockquote>\n","pubDate":"Sat, 09 Jul 2022 00:38:31 +0000","link":"https:\/\/meh.dev\/package-fork-with-composer","guid":"https:\/\/meh.dev\/package-fork-with-composer"},{"title":"PHPStyle: Making PHP Prettier","description":"<p>Why doesn\u2019t PHP have more uniform, opinionated, styles? We see it with so many other languages\u2026<\/p>\n\n<ul>\n  <li>Go: <a href=\"https:\/\/go.dev\/blog\/gofmt\">gofmt<\/a> automatically formats Go source code. According to go.dev, 70% of Go packages in the wild are formatted with gofmt.<\/li>\n  <li>JavaScript: <a href=\"https:\/\/prettier.io\/\">Prettier<\/a> is an opinionated code formatted used by many packages.<\/li>\n  <li>Python: <a href=\"https:\/\/pypi.org\/project\/black\/\">Black<\/a>, the uncompromising Python code formatter.<\/li>\n  <li>C#: A decent formatter is built into Visual Studio, nothing additional required. Or you can go with a more opinionated option like <a href=\"https:\/\/github.com\/belav\/csharpier\">CSharpier<\/a>.<\/li>\n  <li>Java: There\u2019s lots of options for Java. For instance, a popular option is <a href=\"https:\/\/github.com\/google\/google-java-format\">Google\u2019s standard formatter<\/a>.<\/li>\n<\/ul>\n\n<h2 id=\"what-options-do-php-developers-have\">What options do PHP developers have?<\/h2>\n\n<p>In typical PHP fashion, we have a lot choices\u2026 This is a blessing and a curse working in such a mature language. I uncovered while <a href=\"php-static-analysis-tools\">researching static analysis tools<\/a> that there are two dominate tools for styling php, neither very opinionated out of the box.<\/p>\n\n<p><strong><a href=\"https:\/\/github.com\/squizlabs\/PHP_CodeSniffer\">PHP_CodeSniffer<\/a> &amp; <a href=\"https:\/\/cs.symfony.com\/\">PHP-CS-Fixer<\/a><\/strong><\/p>\n\n<p>The main difference between these two is that PHP-CS-Fixer is fully automated. Once it\u2019s configured any issues it finds it will always fix.<\/p>\n\n<p>PHP_CodeSniffer is more likely to tell you what\u2019s wrong and may not be able to fix issues. You can have it try to fix issues by running it\u2019s alt command, phpcbf, but this only works half of the time.<\/p>\n\n<p>I won\u2019t get into specific details about how to install or run these tools, the documentation is great if you want to check that out. But I will briefly explain how these are configured to give more context.<\/p>\n\n<h3 id=\"php_codesniffer-configuration\">PHP_CodeSniffer Configuration<\/h3>\n\n<p>PHP_CodeSniffer is the original style tool for PHP, it\u2019s been around since 2009!<\/p>\n\n<p>Configured with a <code>phpcs.xml<\/code> file or a <code>phpcs.xml.dist<\/code> file, this project is highly un-opinionated. The most standardized configs are the PSR styles included with the package, however many projects override these defaults and in general projects that use phpcs do not share a common style.<\/p>\n\n<p>Given it\u2019s age it\u2019s not surprising that it uses XML for configuration. Back in 2009, everything was configured with XML. <a href=\"https:\/\/github.com\/squizlabs\/PHP_CodeSniffer\/blob\/ed83c67a1dc21096a8b31c5426a541eb2b42f176\/phpcs.xml.dist\">Checkout the config used by the PHP_CodeSniffer project itself as an example.<\/a><\/p>\n\n<p>Checkout some configs of a few large projects<\/p>\n<ul>\n  <li><a href=\"https:\/\/github.com\/WordPress\/wordpress-develop\/blob\/2e5de394c7ce31700c722ebe42e7011a4c293b33\/phpcs.xml.dist\">Wordpress<\/a><\/li>\n  <li><a href=\"https:\/\/github.com\/doctrine\/orm\/blob\/1ffb9152f76bd3d332d5275bd2272d3984bcf3d6\/phpcs.xml.dist\">Doctrine<\/a><\/li>\n  <li><a href=\"https:\/\/github.com\/wikimedia\/mediawiki\/blob\/master\/.phpcs.xml\">MediaWiki<\/a><\/li>\n  <li><a href=\"https:\/\/github.com\/drupal\/core\/blob\/807eaa93fd4a1df145d656bad75641b1e2396849\/phpcs.xml.dist\">Drupal<\/a><\/li>\n<\/ul>\n\n<h3 id=\"configuring-php-cs-fixer\">Configuring PHP-CS-Fixer<\/h3>\n\n<p>PHP-CS-Fixer is the new kid on the block. It was release in 2015 and is developed by Sensio Labs who also develops Symfony, one of the most popular PHP frameworks. It\u2019s also fairly un-opinionated, and again, most projects write their own configs.<\/p>\n\n<p>It\u2019s configured with a PHP file, sometimes as <code>.php_cs<\/code>, or <code>.php-cs-fixer.dist.php<\/code>, or <code>.php-cs-fixer.php<\/code>. <a href=\"https:\/\/github.com\/FriendsOfPHP\/PHP-CS-Fixer\/blob\/aa805a8a4d8384c71a1367fceb57a081a4d969e4\/.php-cs-fixer.dist.php\">Checkout the config used by the PHP-CS-Fixer project itself as an example.<\/a><\/p>\n\n<p>When I first looked at one of these configurations it looked confusing enough that I didn\u2019t even <em>try<\/em> to use this tool. The setup also isn\u2019t straightforward. However, it\u2019s not as scary as it looks, and this tool does work very well once configured.<\/p>\n\n<p>Checkout some configs of a few large projects<\/p>\n<ul>\n  <li><a href=\"https:\/\/github.com\/symfony\/symfony\/blob\/86e87a2ded42cfcd3e25b62fa858baadf1e2ff75\/.php-cs-fixer.dist.php\">Symfony<\/a><\/li>\n  <li><a href=\"https:\/\/github.com\/composer\/composer\/blob\/54063964a7eba4fac568894e39a0df3a0a27fa50\/.php-cs-fixer.php\">Composer<\/a><\/li>\n  <li><a href=\"https:\/\/github.com\/guzzle\/guzzle\/blob\/82ca75f0b1f130f018febdda29af13086da5dbac\/.php-cs-fixer.dist.php\">Guzzle<\/a><\/li>\n  <li><a href=\"https:\/\/github.com\/Seldaek\/monolog\/blob\/248673e85824f9f910738d870e73eafc7654213f\/.php_cs\">Monolog<\/a><\/li>\n<\/ul>\n\n<h1 id=\"introducing-phpstyle-\">Introducing PHPStyle \u2728<\/h1>\n\n<p>Confused yet? In my quest for quickly applying styles to my projects I instead discovered that there\u2019s not a single right answer. <strong>PHP developers develop new standards for every project.<\/strong> Sometimes using the same tools, and sometimes giving up and not using any automated tools at all. \ud83d\ude41<\/p>\n\n<p>So I\u2019ve created yet another project to make this journey easier for my own projects, and maybe yours too.<\/p>\n\n<p>PHPStyle is easy to use, opinionated, and configured with a Neon\/Yaml file (not PHP or XML). It uses PHP-CS-Fixer under the hood so as that project improves this one will as well.<\/p>\n\n<h2 id=\"how-to-use-phpstyle\">How to use PHPStyle<\/h2>\n\n<ol>\n  <li>Require the package<\/li>\n<\/ol>\n\n<pre><code class=\"language-bash\">composer require jspaetzel\/phpstyle --dev\n<\/code><\/pre>\n\n<ol>\n  <li>Run the <a href=\"https:\/\/github.com\/jspaetzel\/phpstyle\/blob\/main\/phpstyle-setup\">setup script<\/a>:<\/li>\n<\/ol>\n\n<pre><code class=\"language-bash\">.\/vendor\/bin\/phpstyle-setup\n<\/code><\/pre>\n\n<blockquote>\n  <p>\ud83d\uddd2 Note: This script is for convenience, you can alternatively do the same steps manually by <a href=\"https:\/\/cs.symfony.com\/#installation\">installing php-cs-fixer<\/a> and copying <a href=\"https:\/\/github.com\/jspaetzel\/phpstyle\/blob\/main\/.php-cs-fixer.dist.php\">this .php-cs-fixer.dist.php file<\/a> to your project root.<\/p>\n<\/blockquote>\n\n<ol>\n  <li>Create\/review the <code>phpstyle.neon<\/code> configuration file. Feel free to make changes to this file again at any time.<\/li>\n<\/ol>\n\n<pre><code class=\"language-yaml\">parameters:\n    php: 7.4\n    risky: false\n    paths:\n        - src\n        - tests\n    excludePaths:\n        - src\/path\/you\/want\/to\/skip\n        - src\/or\/a\/file-to-skip.php\n<\/code><\/pre>\n\n<ol>\n  <li>Run php-cs-fixer to fix your code<\/li>\n<\/ol>\n\n<pre><code class=\"language-bash\">.\/vendor\/bin\/php-cs-fixer fix\n<\/code><\/pre>\n\n<blockquote>\n  <p>\ud83d\uddd2 Note: php-cs-fixer is integrated with PHPStorm and other editors and so PHPStyle should work with them as well.<\/p>\n<\/blockquote>\n\n<p>That\u2019s it, your code is styled!<\/p>\n","pubDate":"Fri, 08 Apr 2022 03:17:34 +0000","link":"https:\/\/meh.dev\/phpstyle-making-php-prettier","guid":"https:\/\/meh.dev\/phpstyle-making-php-prettier"},{"title":"Developing with PHPStorm and Laravel Homestead on Windows","description":"<p>It\u2019s the end of 2021, and we can finally port our development environments. You might be thinking, \u201cjust use docker\u201d right? That\u2019s part of the solution, but the missing piece for a while has been a way to run the development environment directly along-side the application so that you can have a seamless experience when debugging.<\/p>\n\n<p>You\u2019ve been able to do this for some time using VSCode, via it\u2019s <a href=\"https:\/\/code.visualstudio.com\/docs\/remote\/remote-overview\">Remote Development<\/a> feature. Recently Jetbrains <a href=\"https:\/\/blog.jetbrains.com\/blog\/2021\/11\/29\/introducing-remote-development-for-jetbrains-ides\/\">announced similar functionality<\/a>. Let\u2019s see what this feature can do!<\/p>\n\n<p>I want to be able to develop on Linux, Mac, or Windows, so I\u2019ll need a workflow that works for all of those.<\/p>\n\n<p>The project im testing this with I started development on my Linux machine about a year ago.<\/p>\n<ul>\n  <li>PHP 7.4<\/li>\n  <li>Symfony 5<\/li>\n  <li>MySQL<\/li>\n  <li>Nginx<\/li>\n  <li>xDebug<\/li>\n  <li>PHPUnit<\/li>\n  <li>NPM &amp; Webpack Dev Server<\/li>\n  <li>React<\/li>\n<\/ul>\n\n<p>In order to support another developer I switched the project over to <a href=\"https:\/\/laravel.com\/docs\/8.x\/homestead\">Homestead<\/a>. I chose Homestead over Docker containers mainly because Homestead comes pre-baked with all the dependencies I need, and it was easy to set up.<\/p>\n\n<p>The only interesting part of the Homestead.yaml is the <code>folders<\/code> section which I optimized for filesystem performance reasons. I\u2019m only allowing syncing of files which I intend to commit since all other operations will happen within the VM.<\/p>\n\n<pre><code class=\"language-yaml\">folders:\n  -\n    map: .\/\n    to: \/home\/vagrant\/code\n    type: \"rsync\"\n    options:\n      :type: \"rsync\"\n      :rsync__exclude: [\n          \".git\/\",\n          \"vendor\/\",\n          \"node_modules\/\",\n          \"var\/\",\n          \"public\/build\/\",\n          \"public\/uploads\/\"\n      ]\n<\/code><\/pre>\n\n<h1 id=\"running-the-development-environment\">Running the Development Environment<\/h1>\n<p>First we run vagrant like normal. <code>vagrant up<\/code><\/p>\n\n<p>Then we set up the IDE. This is a little different from what we\u2019re used to in PHPStorm.<\/p>\n\n<p><img src=\".\/assets\/remote-dev\/welcome.png\" alt=\"New Welcome Screen\" \/><\/p>\n\n<p><img src=\".\/assets\/remote-dev\/connection.png\" alt=\"Connection Settings\" \/><\/p>\n\n<p><img src=\".\/assets\/remote-dev\/downloadstart.png\" alt=\"Download &amp; Start\" \/><\/p>\n\n<blockquote>\n  <p>\u26a0 When I first started running this I had to increase the resources available. A minimum of 4G is recommended for memory. I increased my Homestead.yaml to have <code>memory: 4096<\/code> and <code>cpus: 4<\/code> which works well for my application.<\/p>\n<\/blockquote>\n\n<p>Once started we can use the IDE pretty much like normal except it\u2019s running INSIDE the VM. This means everything we see is \u201clocal\u201d to the VM where the IDE is also running. The editor you see is just a dumb pane of glass.<\/p>\n\n<p>Some advantages come from this. The most obvious is that the PHP interpreter is local, so you don\u2019t have to create a new tunnel when debugging. This makes everything a little faster. The other obvious advantage I\u2019ve found is that dependencies are \u201creal\u201d. What I mean by this is the files you see in the IDE are the ones being used by the project, and they don\u2019t have to be synchronized to the host machine. This results in more accurate and more performant analysis by the IDE since there\u2019s no file synchronization needed for large directories like the node_modules and vendor folders. It also prevents weird behavior between Vagrant host machines. For instance, the node_modules folder often causes bugs on Windows machines due to directory depth. That\u2019s no longer a potential problem.<\/p>\n\n<p>You also run the npm dev server within the VM, everyone uses the same npm version from the VM and setup of Javascript debugging is easier too since it\u2019s running locally too.<\/p>\n\n<p>This feature is still in \u201cBeta\u201d but I\u2019m really liking what im seeing here. So far it feels really polished and solves a huge gap in functionality for the fullstack multi-platform development that I like to do. The thing I like most about this particular solution is that it all can run locally. Sure, you can also connect it to a cloud provider, but I like having the option to run everything without an internet connection.<\/p>\n","pubDate":"Tue, 28 Dec 2021 01:47:45 +0000","link":"https:\/\/meh.dev\/laravel-homestead-with-phpstorm-remote-dev","guid":"https:\/\/meh.dev\/laravel-homestead-with-phpstorm-remote-dev"},{"title":"The top PHP static code analysis tools of 2026","description":"<p>The PHP community has a diverse ecosystem of static analysis tools which can make it somewhat difficult to decide which tools to use. Hopefully this post helps you decide which of these tools you\u2019ll use.<\/p>\n\n<p>This post separates these tools into one of two types depending on their purpose. The first section is static analysis for the purpose of identifying bugs. The later section is for maintenance of code style\/formatting.<\/p>\n\n<h2 id=\"static-code-analysis-tools\">Static Code Analysis Tools<\/h2>\n<ul>\n  <li>\n    <p><a href=\"https:\/\/github.com\/phpstan\/phpstan\">PHPStan<\/a> is the most commonly used tool and also one of the youngest. It has been rapidly adopted since it\u2019s release in 2016. It\u2019ll discover bugs in your code without running the code.<\/p>\n  <\/li>\n  <li>\n    <p><a href=\"https:\/\/github.com\/vimeo\/psalm\">Psalm<\/a> was also released in 2016 and has grown in popularity a little more slowly. It claims more features out of the box and has a focus is on type-related bugs.<\/p>\n  <\/li>\n  <li>\n    <p><a href=\"https:\/\/scrutinizer-ci.com\/\">Scrutinizer<\/a> is the most popular commercial option in use by open-source projects and has been around longer then its open-source counterparts. It\u2019s free for open-source projects but is only available as a hosted solution.<\/p>\n  <\/li>\n<\/ul>\n\n<p>Let\u2019s look at what some popular open-source PHP project use. This table only includes repositories which have at least 1K GitHub stars, and which implement the static analysis tools as a component of their continuous integration systems.<\/p>\n\n<table>\n  <thead>\n    <tr>\n      <th>\u00a0<\/th>\n      <th>\u2b50<\/th>\n      <th><a href=\"https:\/\/github.com\/phpstan\/phpstan\">PHPStan<\/a><\/th>\n      <th><a href=\"https:\/\/github.com\/vimeo\/psalm\">Psalm<\/a><\/th>\n      <th><a href=\"https:\/\/scrutinizer-ci.com\/\">Scrutinizer<\/a><\/th>\n    <\/tr>\n  <\/thead>\n  <tbody>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/laravel\/framework\">Laravel<\/a><\/td>\n      <td>34.6K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/nextcloud\/server\">NextCloud<\/a><\/td>\n      <td>34.4K<\/td>\n      <td>\u00a0<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u2714\ufe0f<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/symfony\/symfony\">Symfony<\/a><\/td>\n      <td>31K<\/td>\n      <td>\u00a0<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/composer\/composer\">Composer<\/a><\/td>\n      <td>29.4K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/guzzle\/guzzle\">Guzzle<\/a><\/td>\n      <td>23.5K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/Seldaek\/monolog\">Monolog<\/a><\/td>\n      <td>21.4K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/sebastianbergmann\/phpunit\">PHPUnit<\/a><\/td>\n      <td>20K<\/td>\n      <td>\u00a0<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/nikic\/PHP-Parser\">PHP-Parser<\/a><\/td>\n      <td>17.4K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/briannesbitt\/Carbon\">Carbon<\/a><\/td>\n      <td>16.6K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/yiisoft\/yii2\">Yii2<\/a><\/td>\n      <td>14.3K<\/td>\n      <td>\u00a0<\/td>\n      <td>\u00a0<\/td>\n      <td>\u2714\ufe0f<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/PHPOffice\/PhpSpreadsheet\">PhpSpreadsheet<\/a><\/td>\n      <td>13.9K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/thephpleague\/flysystem\">Flysystem<\/a><\/td>\n      <td>13.6K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n      <td>\u2714\ufe0f<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/vlucas\/phpdotenv\">PHPDotEnv<\/a><\/td>\n      <td>13.5K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/slimphp\/Slim\">Slim<\/a><\/td>\n      <td>12.2K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/egulias\/EmailValidator\/\">EmailValidator<\/a><\/td>\n      <td>11.6K<\/td>\n      <td>\u00a0<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/pestphp\/pest\">Pest<\/a><\/td>\n      <td>11.4K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/phalcon\/cphalcon\">Phalcon<\/a><\/td>\n      <td>10.8K<\/td>\n      <td>\ufe0f<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/doctrine\/orm\">Doctrine<\/a><\/td>\n      <td>10.2K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/firebase\/php-jwt\">php-jwt<\/a><\/td>\n      <td>9.5K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/cakephp\/cakephp\">CakePHP<\/a><\/td>\n      <td>8.8K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/twigphp\/Twig\">Twig<\/a><\/td>\n      <td>8.4K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/predis\/predis\">Predis<\/a><\/td>\n      <td>7.8K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/aws\/aws-sdk-php\">AWS SDK<\/a><\/td>\n      <td>6.2K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/elastic\/elasticsearch-php\">Elasticsearch SDK<\/a><\/td>\n      <td>5.3K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/wikimedia\/mediawiki\">Mediawiki<\/a><\/td>\n      <td>5K+<\/td>\n      <td>\u00a0<\/td>\n      <td>\u00a0<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/php-amqplib\/php-amqplib\">php-amqplib<\/a><\/td>\n      <td>4.6K<\/td>\n      <td>\u00a0<\/td>\n      <td>\u00a0<\/td>\n      <td>\u2714\ufe0f<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/stripe\/stripe-php\">Stripe SDK<\/a><\/td>\n      <td>4K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/maxmind\/GeoIP2-php\">Maxmind GeoIP2<\/a><\/td>\n      <td>2.5K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/paratestphp\/paratest\">Paratest<\/a><\/td>\n      <td>2.5K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/getsentry\/sentry-php\">Sentry SDK<\/a><\/td>\n      <td>1.9K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/open-telemetry\/opentelemetry-php\">OpenTelemetry PHP<\/a><\/td>\n      <td>885<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u2714\ufe0f<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/opensearch-project\/opensearch-php\">OpenSearch PHP<\/a><\/td>\n      <td>148<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n  <\/tbody>\n<\/table>\n\n<p>There are some other fairly popular tools worth mentioning but which were not used by enough projects to be included in the table above.<\/p>\n<ul>\n  <li>\n    <p><a href=\"https:\/\/github.com\/phan\/phan\">Phan<\/a> isn\u2019t used by any of the projects here besides MediaWiki that I could find. It was created originally at Etsy and appears to be the primary tool used by Wikipedia\u2019s MediaWiki project. The advertised upside of using Phan is that it has a focus on minimizing false-positives. This makes it trivial to use, but it might catch fewer issues compared to others.<\/p>\n  <\/li>\n  <li>\n    <p><a href=\"https:\/\/www.jetbrains.com\/help\/phpstorm\/code-inspection.html\">PHPStorm Code Inspections<\/a>: The inspection tools built into PHPStorm are impressive and can identify many potential issues without any additional tooling.<\/p>\n  <\/li>\n  <li>\n    <p><a href=\"https:\/\/github.com\/phpmd\/phpmd\">PHP Mess Detector<\/a>: This is an older static analysis tool which offers some different functionality. Besides identifying potential bugs it also can help identifying generally poor code. It\u2019s very mature and may be more useful for targeted project analysis.<\/p>\n  <\/li>\n  <li>\n    <p><a href=\"https:\/\/www.sonarqube.org\/\">Sonarqube<\/a>: Sonarqube is a commercial static analysis product. It\u2019s community edition is good at detecting bugs, vulnerabilities, and generally for improving code quality. They also provides an <a href=\"https:\/\/www.sonarlint.org\/\">IDE extension, Sonarlint<\/a> which works well to supplement the CI offerings.<\/p>\n  <\/li>\n  <li>\n    <p><a href=\"https:\/\/github.com\/kalessil\/phpinspectionsea\">PHP Inspections (EA Extended)<\/a>: This is a plugin for IntelliJ\/PHPStorm which supplements the inspections built into PHPStorm.<\/p>\n  <\/li>\n  <li>\n    <p><a href=\"https:\/\/www.jetbrains.com\/qodana\/\">Qodana<\/a>: This is a newer commercial product from JetBrains which is still in early access. It\u2019s a hosted solution which can be used to analyze code and identify bugs and vulnerabilities.<\/p>\n  <\/li>\n  <li>\n    <p><a href=\"https:\/\/mago.carthage.software\/\">Mago<\/a>: A newer tool written in Rust that combines static analysis, linting, and formatting into a single high-performance package. It aims to provide exceptional speed and a unified developer experience.<\/p>\n  <\/li>\n<\/ul>\n\n<h2 id=\"style-tools\">Style Tools<\/h2>\n\n<ul>\n  <li>\n    <p><a href=\"https:\/\/github.com\/squizlabs\/PHP_CodeSniffer\">PHPCS<\/a> is the original code standards tool for PHP and dates back to 2006. It\u2019s primary use is to establish standards and identify violations. It does however also provide a package <code>phpcbf<\/code> which can sometimes automatically fix violations. PHPCS is extremely mature and very flexible and comes with a massive selection of pre-written \u201cSniffs\u201d available to use.<\/p>\n  <\/li>\n  <li>\n    <p><a href=\"https:\/\/github.com\/FriendsOfPHP\/PHP-CS-Fixer\">PhpCsFixer<\/a> is supported by the popular Symfony framework. This tool automatically applies any defined code styles to code when it\u2019s run. e.g. <code>php-cs-fixer fix src<\/code>. This seems to be the leading choice for a majority of projects.<\/p>\n  <\/li>\n<\/ul>\n\n<p>Some other interesting style tools I found that weren\u2019t heavily in use while reviewing these projects<\/p>\n<ul>\n  <li><a href=\"https:\/\/github.com\/marketplace\/actions\/composer-normalize-action\">Composer Normalize Action<\/a> Tool to keep your composer file formatted consistently<\/li>\n<\/ul>\n\n<p>Here\u2019s a selection of some popular packages today.<\/p>\n\n<table>\n  <thead>\n    <tr>\n      <th>\u00a0<\/th>\n      <th>\u2b50<\/th>\n      <th><a href=\"https:\/\/github.com\/FriendsOfPHP\/PHP-CS-Fixer\">PhpCsFixer<\/a><\/th>\n      <th><a href=\"https:\/\/github.com\/squizlabs\/PHP_CodeSniffer\">PHPCS<\/a><\/th>\n    <\/tr>\n  <\/thead>\n  <tbody>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/laravel\/framework\">Laravel<\/a><\/td>\n      <td>34.6K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/nextcloud\/server\">NextCloud<\/a><\/td>\n      <td>34.4K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/symfony\/symfony\">Symfony<\/a><\/td>\n      <td>31K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/composer\/composer\">Composer<\/a><\/td>\n      <td>29.4K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/guzzle\/guzzle\">Guzzle<\/a><\/td>\n      <td>23.5K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/Seldaek\/monolog\">Monolog<\/a><\/td>\n      <td>21.4K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/WordPress\/WordPress\">Wordpress<\/a><\/td>\n      <td>21K+<\/td>\n      <td>\u00a0<\/td>\n      <td>\u2714\ufe0f<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/sebastianbergmann\/phpunit\">PHPUnit<\/a><\/td>\n      <td>20K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/nikic\/PHP-Parser\">PHP-Parser<\/a><\/td>\n      <td>17.4K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/briannesbitt\/Carbon\">Carbon<\/a><\/td>\n      <td>16.6K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/yiisoft\/yii2\">Yii2<\/a><\/td>\n      <td>14.3K<\/td>\n      <td>\u00a0<\/td>\n      <td>\u2714\ufe0f<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/PHPOffice\/PhpSpreadsheet\">PhpSpreadsheet<\/a><\/td>\n      <td>13.9K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u2714\ufe0f<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/thephpleague\/flysystem\">Flysystem<\/a><\/td>\n      <td>13.6K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/slimphp\/Slim\">Slim<\/a><\/td>\n      <td>12.2K<\/td>\n      <td>\u00a0<\/td>\n      <td>\u2714\ufe0f<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/phalcon\/cphalcon\">Phalcon<\/a><\/td>\n      <td>10.8K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u2714\ufe0f<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/doctrine\/orm\">Doctrine<\/a><\/td>\n      <td>10.2K<\/td>\n      <td>\u00a0<\/td>\n      <td>\u2714\ufe0f<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/firebase\/php-jwt\">php-jwt<\/a><\/td>\n      <td>9.5K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/cakephp\/cakephp\">CakePHP<\/a><\/td>\n      <td>8.8K<\/td>\n      <td>\u00a0<\/td>\n      <td>\u2714\ufe0f<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/twigphp\/Twig\">Twig<\/a><\/td>\n      <td>8.4K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/predis\/predis\">Predis<\/a><\/td>\n      <td>7.8K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/wikimedia\/mediawiki\">Mediawiki<\/a><\/td>\n      <td>5K+<\/td>\n      <td>\u00a0<\/td>\n      <td>\u2714\ufe0f<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/php-amqplib\/php-amqplib\">php-amqplib<\/a><\/td>\n      <td>4.6K<\/td>\n      <td>\u00a0<\/td>\n      <td>\u2714\ufe0f<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/drupal\/drupal\">Drupal<\/a><\/td>\n      <td>4.3K+<\/td>\n      <td>\u00a0<\/td>\n      <td>\u2714\ufe0f<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/stripe\/stripe-php\">Stripe SDK<\/a><\/td>\n      <td>4K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/maxmind\/GeoIP2-php\">Maxmind GeoIP2<\/a><\/td>\n      <td>2.5K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/paratestphp\/paratest\">Paratest<\/a><\/td>\n      <td>2.5K<\/td>\n      <td>\u00a0<\/td>\n      <td>\u2714\ufe0f<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/getsentry\/sentry-php\">Sentry SDK<\/a><\/td>\n      <td>1.9K<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/open-telemetry\/opentelemetry-php\">OpenTelemetry PHP<\/a><\/td>\n      <td>885<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u2714\ufe0f<\/td>\n    <\/tr>\n    <tr>\n      <td><a href=\"https:\/\/github.com\/opensearch-project\/opensearch-php\">OpenSearch PHP<\/a><\/td>\n      <td>148<\/td>\n      <td>\u2714\ufe0f<\/td>\n      <td>\u00a0<\/td>\n    <\/tr>\n  <\/tbody>\n<\/table>\n\n<h3 id=\"phpcsfixer-utilities\">PhpCsFixer Utilities<\/h3>\n<p>PhpCsFixer isn\u2019t the most opinionated tool. A couple of projects have cropped up that ship opinionated configurations for this tool.<\/p>\n<ul>\n  <li><a href=\"https:\/\/github.com\/laravel\/pint\">Pint<\/a> opinionated php-sc-fixer by the Laravel team.<\/li>\n  <li><a href=\"https:\/\/styleci.io\/\">Styleci<\/a> a hosted version fixer by the Laravel team. It fixes styles quietly in the background as developers make changes by pulling the code, restyling it, and pushing it back to the main repository.<\/li>\n  <li><a href=\"https:\/\/php.style\/\">PHP.Style<\/a> my own opinionated configuration.<\/li>\n<\/ul>\n\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>The most popular tools to use overall appear are PHPStan &amp; PhpCsFixer.<\/p>\n\n<p>However, it\u2019s not a one size fit all solution. Psalm has strong popularity and is often used alongside PHPStan. And for enforcing code standards, PhpCsFixer is the most popular, but it\u2019s still often paired with PHPCS which has more complex configuration options.<\/p>\n\n<p>I found this analysis interesting when reviewing these tools for my own usage. If you have feedback or can correct me about any of the information in this post please leave me a comment.<\/p>\n\n<h2 id=\"news--changelog\">News &amp; Changelog<\/h2>\n<p>This post is periodically updated to keep up with the latest trends.<\/p>\n\n<h3 id=\"jan-2026\">Jan 2026<\/h3>\n<ul>\n  <li>PER Coding Style 3.0 was released<\/li>\n<\/ul>\n\n<h3 id=\"jan-2025\">Jan 2025<\/h3>\n<ul>\n  <li>Audited the status of all projects<\/li>\n  <li>Removed style ci from style tools table<\/li>\n  <li>PHPStan picked up a few new projects this year and continues growing in popularity<\/li>\n  <li>PHPStan released <a href=\"https:\/\/phpstan.org\/blog\/phpstan-2-0-released-level-10-elephpants\">2.0 version<\/a> in november. First major release in a while.<\/li>\n  <li>PHPCS picked up a few new projects from this sample group and is still going strong though PhpCsFixer remains the more popular choice overall.<\/li>\n  <li>Added a few popular projects.<\/li>\n<\/ul>\n\n<h3 id=\"jan-2024\">Jan 2024<\/h3>\n<ul>\n  <li>Updated the status of all projects on this page. Added a few newer notable projects.<\/li>\n  <li>PHPStan is up one and Psalm is down on the same project. Removed Phan from the table which is still only used by MediaWiki.<\/li>\n  <li>Jetbrains is previewing a new code analysis tool, Qodana, which is now included in the list. Only one project I checked is using it so far.<\/li>\n  <li>Things to look forward to this year: PHPStan 2.0 &amp; expanded coding style standards w\/ PERCS 2.0: https:\/\/www.php-fig.org\/per\/coding-style\/<\/li>\n<\/ul>\n\n<h3 id=\"jan-2023\">Jan 2023<\/h3>\n<ul>\n  <li>Updated the status of all projects on this page. Most projects gained some popularity but order stayed roughly the same.<\/li>\n  <li>PHPStan, PHP-CS-Fixer, and Scrutinizer grew in popularity.<\/li>\n  <li>Add section about php-cs-fixer utilities.<\/li>\n<\/ul>\n\n<h3 id=\"jan-2022\">Jan 2022<\/h3>\n<p>Checked the status of all projects in these lists and updated stats. Psalm continues to grow in popularity, a couple projects added it since they were last checked. Otherwise, no major changes.<\/p>\n\n<h3 id=\"march-2021\">March 2021<\/h3>\n<p>First version of this post<\/p>\n","pubDate":"Thu, 11 Mar 2021 09:38:15 +0000","link":"https:\/\/meh.dev\/php-static-analysis-tools","guid":"https:\/\/meh.dev\/php-static-analysis-tools","category":["PHP","CI","Static Analysis"]},{"title":"2020 Retrospective","description":"<p>It\u2019s 2021 and time to reflect on 2020. Sometimes I start these posts early in December but this year I was so busy throughout December that I didn\u2019t even think about it until mid-January.<\/p>\n\n<p>This post is part of a series, want to look back further? Checkout my previous years:<\/p>\n<ul>\n  <li><a href=\"2019-reflection\">2019 Reflection<\/a><\/li>\n  <li><a href=\"2018-retrospective\">2018 Retrospective<\/a><\/li>\n  <li><a href=\"2017-wear-flowers-in-your-hair\">2017 Wear some flowers in your hair<\/a><\/li>\n  <li><a href=\"2016-a-full-stack-year\">2016 A Full Stack Year<\/a><\/li>\n<\/ul>\n\n<h2 id=\"what-happened-in-2020\">What happened in 2020?<\/h2>\n<p>Overall 2020 was not bad for me!<\/p>\n\n<p>I spent a lot of time at home, I walked around and truly enjoyed my neighborhood. It\u2019s quirky and beautiful!<\/p>\n\n<div class=\"quoted-image\" style=\"width:px;\">\n    <img src=\"\/assets\/2020-retro\/collage-neighborhood.jpg\" alt=\"A few shots from around Castro\" \/>\n    <span>A few shots from around Castro<\/span>\n<\/div>\n\n<p>I still traveled some. I <a href=\"maui-during-pandemic\">made it Maui<\/a> with my roommate. I also did a fair bit of domestic travel which included LOTS of driving. I went with my sister on our first cross country road-trip from D.C. to San Francisco.<\/p>\n\n<div class=\"quoted-image\" style=\"width:px;\">\n    <img src=\"\/assets\/2020-retro\/2020-map.png\" alt=\"A map of all of my 2020 Travel\" \/>\n    <span>A map of all of my 2020 Travel<\/span>\n<\/div>\n\n<p>I shutdown a long term <a href=\"era-of-hosting\">side project<\/a>. Closing it down was hard but worthwhile. I\u2019ve got <a href=\"enlio\">one more long-term project<\/a> that I\u2019m shuttering before the end of 2021. I\u2019m hopeful that having fewer ongoing projects will give me more time to start some a new one.<\/p>\n\n<p>I increased my open-source contributions in 2020 and <a href=\"https:\/\/github.com\/federico-terzi\/modulo\">contributed<\/a> <a href=\"https:\/\/github.com\/lightSAML\/lightSAML\">to<\/a> <a href=\"https:\/\/github.com\/symfony\/symfony-docs\">several<\/a> <a href=\"https:\/\/github.com\/autokey\/autokey\">projects<\/a> that aren\u2019t my own. This is the first year where I have felt truly confident when contributing to other open-source projects and I want to continue this in 2021 with more substantial contributions.<\/p>\n\n<p>My goals for 2020 were pretty slim, which is fortunate since 2020 went off the rails. Here\u2019s how I did..<\/p>\n<ul>\n  <li>Promoted to Senior Software Engineer at work. \u2714\ufe0f<\/li>\n  <li>Health-wise I failed, I stopped biking to work when we started working from home and I haven\u2019t been to the gym. I have been running occasionally but less then previously. \u274c<\/li>\n  <li>I re-modeled my personal website some, there\u2019s still room for improvement but there always will be. I cheated a little and switched domains while writing this post so johnspaetzel.com is now meh.dev. \u2714\ufe0f<\/li>\n<\/ul>\n\n<h2 id=\"whats-next-for-2021\">What\u2019s next for 2021?<\/h2>\n\n<p>I don\u2019t really know, the pandemic is still raging, and although a vaccine is now being distributed it\u2019ll be a while before things return fully to normal. As I write this the bay area is just coming down from it\u2019s highest case surge.<\/p>\n\n<div class=\"quoted-image\" style=\"width:px;\">\n    <img src=\"\/assets\/2020-retro\/covid-cases.png\" alt=\"Graph of new COVID-19 Cases in the Bay Area as of January 25th, 2021\" \/>\n    <span>Graph of new COVID-19 Cases in the Bay Area as of January 25th, 2021<\/span>\n<\/div>\n\n<p>I\u2019m hoping to travel more internationally but probably not until the later half of the year. I\u2019m looking forward to visiting my family again in Florida hopefully at some point as well, though I\u2019ll probably wait to be vaccinated before visiting.<\/p>\n\n<p>In December I worked on some new investments, consolidated work &amp; personal cell phones, and I switched health insurance providers to a lower cost more convenient option. Basically I decided it\u2019s a good time to stop procrastinating. Continuing this trend in January I want to spend time spring cleaning and sell or donate anything that I haven\u2019t used in the last few years. I also want to do do some upgrades to my home office such as get a new webcam, mic, and maybe new desk.<\/p>\n\n<blockquote>\n  <p>I decided it\u2019s a good time to stop procrastinating.<\/p>\n<\/blockquote>\n\n<p>I want to <strong>simplify my life<\/strong> and switch operating systems so that I use them same one for personal and work. Context switching between MacOS, Windows, and Ubuntu has made life more difficult then I like to admit. Even for simple things like writing this post, <strong>fragmentation made things more difficult<\/strong> since my build tool-chain wasn\u2019t readily available in Windows.<\/p>\n\n<p>I\u2019ve been working from home for the last 9 months since our offices closed. I still haven\u2019t quite settled into a work routine that works well. This isn\u2019t ideal and I need to make more of an effort to <strong>create separate space for work<\/strong>.<\/p>\n\n<p>I still want to exercise more regularly and develop some good habits, one of these that I think I\u2019ll need to make an effort on is to <strong>do regular meal planning<\/strong>. Since im working from home every day it would be quite a bit better if I cooked at home regularly and ordered less take-out.<\/p>\n\n<h1 id=\"in-conclusion\">In\u2026 Conclusion?<\/h1>\n\n<p>2021 feels like a continuation of 2020 which was put on hold by the pandemic. I think 2021 will be an optimization cycle without really specific goals. I want to upgrade all the little things in my life that could be better so that when opportunity or inspiration strikes I\u2019ll be at my best.<\/p>\n","pubDate":"Mon, 25 Jan 2021 22:05:40 +0000","link":"https:\/\/meh.dev\/2020-retro","guid":"https:\/\/meh.dev\/2020-retro","category":"Year in Review"},{"title":"I visited Maui during the pandemic","description":"<p>I\u2019d never been to Hawaii or Maui before this year even though it\u2019s a meer 5 hour flight and since I hadn\u2019t done very much travel otherwise this year I jumped at the opportunity when my roommate suggested we go.<\/p>\n\n<p>Because of the COVID-19 pandemic it was particularly cheap to travel and staying on the island was also cheaper then usual. Hawaii has been particularly cautious about trying to prevent the spread of the virus, and their efforts have been effective. Most of the Hawaiian islands have a dramatically lower infection-rate then the rest of the United States.<\/p>\n\n<p>Hawaii should be extra cautious. They\u2019re isolated and have much more control over who comes in and out of the islands compared to most states. They also have extra incentive to protect residents. Hawaii\u2019s population is steadily aging at a faster rate then most of the United States. <a href=\"https:\/\/web.archive.org\/web\/20201017121959\/https:\/\/census.hawaii.gov\/wp-content\/uploads\/2020\/06\/Hawaii-Population-Characteristics-2019.pdf\">From 2010 to 2019 the elderly population (65+) of Hawaii grew by 62.3%<\/a>. This older population is also disproportionately affected by the pandemic which are <a href=\"https:\/\/web.archive.org\/web\/20201201020338\/https:\/\/www.cdc.gov\/coronavirus\/2019-ncov\/need-extra-precautions\/older-adults.html\">90x more at risk of dying from COVID-19<\/a> compared to young adults.<\/p>\n\n<p><img src=\".\/assets\/maui\/covid_jeep.jpg\" alt=\"COVID Jeep\" \/><\/p>\n\n<h1 id=\"getting-there-safely\">Getting there safely<\/h1>\n\n<p>As the pandemic as worn on they\u2019ve found ways of letting individuals visit safely. The way we traveled required us to \u201cArrive Healthy\u201d and prove that by having a test within 72 hours of traveling. Once the results were received we then had to submit the test results on the Hawaii state \u201cSafe Travels\u201d website and await review.<\/p>\n\n<blockquote>\n  <p>Arrive Healthy: Mandatory pre-departure test submitted 72 hours prior to departing for Maui County.<\/p>\n<\/blockquote>\n\n<p>When our flight landed we were still awaiting test results so we took a Lyft directly to where we were staying.<\/p>\n\n<p>The Hawaiian governor has been writing a series of supplementary <a href=\"https:\/\/web.archive.org\/web\/20201115120318\/https:\/\/governor.hawaii.gov\/wp-content\/uploads\/2020\/03\/2003109-ATG_COVID-19-Supplementary-Proclamation-signed.pdf\">emergency proclamations<\/a> which change policies throughout the islands. One of the policies the Hawaiian government set was to prevent car rentals for anyone who\u2019s not exempt from quarantine. We weren\u2019t aware of this before traveling so were surprised we weren\u2019t able to get a car initially and had to take a fairly expensive Lyft to where we were staying while we waited for test results.<\/p>\n\n<p><img src=\".\/assets\/maui\/quarantine.jpg\" alt=\"Maui Quarantining\" \/><\/p>\n\n<h1 id=\"quaratining\">Quaratining<\/h1>\n\n<p>We wound up having to Quarantine for about 2 days.<\/p>\n\n<p>Test results can vary in how long they take to be returned, in our case we were expecting them to come through within 24 - 72 hours so we took the tests about 48 hours before traveling. My results wound up taking about 72 hours and my roommate\u2019s took almost a day longer then that. Apparently some results can take as long as 14 days though so there\u2019s some risk of getting stuck waiting.<\/p>\n\n<blockquote>\n  <p>Some test results can take as long as 14 days to be processed by labs<\/p>\n<\/blockquote>\n\n<p>While Quarantining you\u2019re not allowed to leave to buy groceries or food. Delivery services on the islands can be lacking but there are a few services that provide delivery of most items. I wish I knew about <a href=\"https:\/\/www.fooddeliverymaui.com\/\">808 PickUps<\/a> sooner which is a local business like DoorDash but with a larger local selection. Other services like Instacart also work fine for larger grocery deliveries.<\/p>\n\n<p><img src=\".\/assets\/maui\/quarantine_2.jpg\" alt=\"Maui Quarantining 2\" \/><\/p>\n\n<h1 id=\"exploring-the-island\">Exploring the island<\/h1>\n\n<p>Once we left quarantine the island was fabulous. We only got stuck in traffic once where there was a construction closure and otherwise much of the island felt like a private paradise. Although it wasn\u2019t busy anywhere many parking lots were still filling up pretty well. I was surprised when locals told us they were only at about 5% of the tourism capacity. I imagine with a full load of visitors it must be hell driving around so I understand some of the local disdain for tourists.<\/p>\n\n<p>We didn\u2019t have any negative experiences on the island but you could tell there was some tension in the air at some venues. Some locals were cautious but most were going about their business like it was any other year. Before we arrived many businesses had only just reopened within the last several weeks, or were still in the process of opening, and just as many were still closed.<\/p>\n\n<p>In the more remote parts of the island and on the beaches we didn\u2019t see many masks at all. This is about what we\u2019d expected but I was still a little surprising considering the otherwise severe rules for entry to the island. At one bar it looked like business as usual, a packed house with no social distancing.<\/p>\n\n<p>I wish I planned for a longer stay, but im back home now and brought COVID back with me. (Just this cute plushie!)<\/p>\n\n","pubDate":"Tue, 01 Dec 2020 22:57:24 +0000","link":"https:\/\/meh.dev\/maui-during-pandemic","guid":"https:\/\/meh.dev\/maui-during-pandemic","category":"Travel"},{"title":"Linuxisms","description":"<p>This is a living post about linux topics which inhibit linux distros from reaching the masses and my take on them. In no particular order\u2026<\/p>\n\n<h1 id=\"packaging\">Packaging<\/h1>\n<p>Having access to quality packages is my favorite things about using linux. But it comes with some issues.<\/p>\n\n<ul>\n  <li>\n    <p>A big issue is the support between distributions. Ubuntu is pretty ubiquitously supported but most distros are not.<\/p>\n  <\/li>\n  <li>\n    <p>For Ubuntu users their distro is most likely supported but the way to install an application is not universal. This is actually getting somewhat worse currently. Many applications are now distributed via snap, apt, AppImages, or a plain old binary.<\/p>\n  <\/li>\n  <li>\n    <p>Another drawback is for maintainers, it\u2019s super complex for a popular package to support every potential distro which takes time away from their project. This makes development less popular and harder for the ones willing to do it.<\/p>\n  <\/li>\n<\/ul>\n\n<h1 id=\"updates\">Updates<\/h1>\n<p>Linux makes updates scarier then they need to be by displaying too much information.<\/p>\n\n<p>Information overload is a real thing and it can scare people away from upgrading or scare people away entirely.<\/p>\n\n<p>One nuisance I have with distribution updates (at least on Ubuntu) is that custom sources added to apt get disabled with each update. Then I have to manually inspect and re-enable each one.<\/p>\n\n<p><img src=\".\/assets\/linuxisms\/ubuntu-install.png\" alt=\"Ubuntu Updater\" \/><\/p>\n\n<p>I actually kinda enjoy seeing what\u2019s being updated, but this is somewhat a regression for Google Chrome which receives updates so regularly. On Windows and Linux it just updates itself and occasionally prompts to be restarted. On linux having to wait for me to trigger it\u2019s update besides being inconvenient is also a security hazard. \ud83d\udc4e<\/p>\n\n<h1 id=\"graphics-drivers\">Graphics drivers<\/h1>\n<p>The graphics drivers on linux are getting pretty great and I think this is almost a solved problem as of 2020.<\/p>\n\n<p>The main hurdle is for proprietary drivers which don\u2019t typically come preinstalled. Some distros are now including these drivers though or have easy way to install them which mostly resolves this issue.<\/p>\n\n<p>Information overload is still an issue with this too. Custom drivers still require the user to click some things to install them, it\u2019s not quite plug &amp; play.<\/p>\n\n<h1 id=\"enterprise\">Enterprise<\/h1>\n<p>Enterprise support is pretty lacking still, a lot of the host management that IT departments want doesn\u2019t work well or isn\u2019t sufficient especially for power users who cant have a totally locked down environment.<\/p>\n","pubDate":"Sat, 14 Nov 2020 19:01:47 +0000","link":"https:\/\/meh.dev\/linuxisms","guid":"https:\/\/meh.dev\/linuxisms","category":"Linux"},{"title":"What i use","description":"<p>This is a living post for what I use frequently, probably everyday. I\u2019ll update this periodically and welcome suggestions for improvements.<\/p>\n\n<h2 id=\"systems\">Systems<\/h2>\n<ul>\n  <li>Lenovo Thinkpad X1 Carbon with Ubuntu 20.10 \u2764\ufe0f<\/li>\n  <li>Macbook Pro with Ubuntu 20.10, Windows 10<\/li>\n  <li>Pixel 3 \u2764\ufe0f<\/li>\n  <li>iPhone 8<\/li>\n  <li>Ubiquity UniFi \u2764\ufe0f<\/li>\n  <li>Sonnet Breakaway box with Nvidia 1070 \u2764\ufe0f<\/li>\n<\/ul>\n\n<p>I prefer to work in Ubuntu but my personal laptop is a Macbook Pro which I got in 2017. It runs Ubuntu pretty well so I\u2019ve kept it around. I also use it for gaming which is why I have a tri-boot configuration.<\/p>\n\n<p>I have an iPhone from work but my main device is the Pixel 3. I\u2019ve kept the iPhone because I like to compare functionality side-by-side and it\u2019s also handy to have a backup device on a different network provider.<\/p>\n\n<h2 id=\"developer-tools\">Developer Tools<\/h2>\n<ul>\n  <li>IDE: PHPStorm \ud83d\udcb2\u2764\ufe0f<\/li>\n  <li>Editor: VS Code<\/li>\n  <li>Terminal: Terminator<\/li>\n<\/ul>\n\n<h2 id=\"software\">Software<\/h2>\n<p>The software listed here are the essentials that are I use everyday that I want to endorse.<\/p>\n\n<ul>\n  <li>Firefox mobile and desktop for my browser<\/li>\n  <li>AutoKey for autocomplete \u2764\ufe0f<\/li>\n  <li>CopyQ for clipboard management<\/li>\n  <li>Dropbox for file storage<\/li>\n  <li>Dropbox Paper for shared notes \u2764\ufe0f<\/li>\n  <li>Flameshot for screenshots<\/li>\n  <li>Authy for 2FA \u2764\ufe0f<\/li>\n  <li>Spotify for music \ud83d\udcb2\u2764\ufe0f<\/li>\n  <li>1Password for passwords \ud83d\udcb2<\/li>\n  <li>Standard Notes for taking notes \ud83d\udcb2\u2764\ufe0f<\/li>\n  <li>Google Keep for notes with reminders<\/li>\n  <li>Google Calendar \u2764\ufe0f<\/li>\n  <li>Google Photos \u2764\ufe0f<\/li>\n  <li>Gmail for email<\/li>\n  <li>Pocket Casts<\/li>\n<\/ul>\n\n<h2 id=\"legend\">Legend<\/h2>\n\n<ul>\n  <li>\ud83d\udcb2 are monthly or annual subscriptions<\/li>\n  <li>\u2764\ufe0f are things that I love<\/li>\n<\/ul>\n","pubDate":"Fri, 26 Jun 2020 07:49:38 +0000","link":"https:\/\/meh.dev\/what-i-use","guid":"https:\/\/meh.dev\/what-i-use","category":"Software Stack"}]}}