Tag: WordPress

  • Using PHPStan in a WordPress project

    Using PHPStan in a WordPress project

    Over the past few years I’ve grown to like PHPStan, a static analysis for PHP projects. It’s a great tool for catching bugs and improving code quality. In this post, I’ll show you how to use PHPStan for WordPress plugin or theme development.

    Yes, I really like PHPStan

    What PHPStan does

    There are many tools for analyzing PHP code tools out there, such as PHP-Parallel-Lint for syntax checks, or PHP_CodeSniffer to check conformance with coding standards. The latter is often used in WordPress projects (including core itself!) thanks to the dedicated WordPress Coding Standards ruleset.

    In addition to these tools, PHPStan tries to find bugs based on the information it derives from typehints and PHPDoc annotations, without actually running your code or writing any tests. Among the things it tests are the existence and accessibility of classes, methods, and functions, argument mismatches, and of course type mismatches.

    Strongly-typed code gives PHPStan more information to work with. Keep in mind that typehinted and annotated code helps both static analysis tools and people understand the code.

    Getting started

    While I try to cover the basics of how to set up PHPStan, I recommend reading the project’s excellent Getting Started guide, which goes more into detail.

    To get the ball rolling, install PHPStan via Composer:

    composer require --dev phpstan/phpstanCode language: Bash (bash)

    After that you could already try running it like this:

    vendor/bin/phpstan analyse src

    Here, src is the folder containing all your plugin’s files.

    I usually prefer setting such commands as Composer scripts so I don’t have to remember them in detail. Plus, the src path can be omitted if it’s defined in a configuration file. But more on that later.

    My typical Composer script looks like this:

    {
      "scripts": {
        "phpstan": "phpstan analyse --memory-limit=2048M"
      }
    }Code language: JSON / JSON with Comments (json)

    Note how I also increase the memory limit. I’ve found this to be necessary for most projects as PHPStan tends to consume quite a lot of memory for larger code bases.

    Work around PHP version requirement

    PHPStan requires PHP >= 7.2 to run, but your actual code does not have to use PHP 7.x. So if your project’s minimum supported PHP version is lower than 7.2, I suggest a small workaround: install PHPStan in a separate directory with its own composer.json file, like build-cs/composer.json.

    This file is separate from your project’s Composer configuration and contains only the dependencies with a higher version requirement:

    {
      "require-dev": {
        "phpstan/phpstan": "^1.10"
      }
    }Code language: JSON / JSON with Comments (json)

    After that, add a script to your main composer.json file to run PHPStan from the newly created directory:

    {
      "scripts": {
        "phpstan": [
          "composer --working-dir=build-cs install",
          "build-cs/vendor/bin/phpstan analyse --memory-limit=2048M"
        ]
      }
    }Code language: JSON / JSON with Comments (json)

    This way, running composer run phpstan will analyze your code as usual, even if the main configuration requires an older PHP version.

    Note: It goes without saying that I highly recommend reconsidering your project’s version requirements. Then you won’t have to add such workarounds.

    Telling PHPStan about WordPress

    Remember how PHPStan analyzes your code to check the existence of classes and functions? It looks for them in all your analyzed files as well as your Composer dependencies. But WordPress core isn’t really a dependency of your WordPress plugin.

    So without any additional configuration PHPStan will first not know about WordPress-specific code like WP_DEBUG and WP_Block. You have to first make it aware of them.

    Thankfully, the php-stubs/wordpress-stubs package provides stub declarations for WordPress core functions, classes and interfaces. They’re like source code, but only the PHPDocs are read from it. However, we are not going to use them directly.

    Instead, you should install the WordPress extension for PHPStan. Not only does it load the php-stubs/wordpress-stubs package, it also defines some core constants, handles special functions like is_wp_error() or get_posts(), and apply_filters() usage.

    Simply require the extension in your project and you are all set:

    composer require --dev szepeviktor/phpstan-wordpress phpstan/extension-installerCode language: JavaScript (javascript)

    No further configuration is needed, phpstan/extension-installer handles discovery automatically.

    Stubs for other WordPress projects

    If your WordPress plugin or theme integrates with other plugins like for example WooCommerce, you will also need to provide stub declarations for them to PHPStan.

    Luckily, the PHP stubs library already provides stubs for WooCommerce, Yoast SEO, ACF Pro and even WP-CLI.

    You can also generate stubs for other projects yourself using the available generator library.

    Baseline configuration

    Now that we have installed all the necessary tools, we can start with our initial configuration.

    Here it’s important to know about the different rule levels PHPStan supports. The default level is 0 and is for the most basic checks. With each level, more checks are added. Level 9 is the strictest.

    If you want to use PHPStan but your codebase isn’t quite there yet, you can start with a lower level and increment it over time.

    Alternatively, you can use PHPStan’s baseline feature to ignore currently reported errors in subsequent runs, focusing only on new and changed code. This way you can check newer code at a higher level, giving you time to fix the existing code later.

    Let’s say we want to start with level 1 and analyze everything in our src folder as mentioned above. Our minimum configuration file will look like this:

    parameters:
      level: 1
      paths:
        - src/Code language: YAML (yaml)

    Save this file as phpstan.neon.dist. PHPStan uses a configuration format called NEON which is similar to YAML, hence the file name.

    This configuration file is also where you point PHPStan to all the different stub declarations if you have any.

    Now you can truly run composer run phpstan and PHPStan will analyze your project while being fully aware about the WordPress context.

    Some errors you might see after your initial run are “If condition is always true” or “Call to an undefined function xyz()”. Some of them are pretty easy to resolve, others require consulting the documentation or the help community.

    Improving PHPDocs

    PHPStan relies on typehints and PHPDoc comments to understand your code. PHPDocs can also provide additional information, such as what’s in an array.

    When there are errors being reported in your existing code base, chances are that the PHPDoc annotations can be improved.

    Learn more about PHPDocs basics and all the types PHPStan understands. It also supports some proprietary @phpstan- prefixed tags that are worth checking out.

    Sometimes you might get errors because of incorrect PHPDocs in one of your dependencies, like WordPress core. In this case, I suggest temporarily ignore the error in PHPStan and submit a ticket and pull request to improve the documentation upstream.

    Usage in REST API code

    A special case for some WordPress projects when it comes to PHPDoc is the WordPress REST API. If you have ever extended it in any way, for example by adding new fields to the schema, you’ll know how the fields available in a WP_REST_Request or WP_REST_Response object are based on that schema (plus some special fields for the former, like context or _embed).

    Let’s say you have some code like this in your REST API controller:

    $per_page = ! empty( $request['per_page'] ) ? $request['per_page'] : 100;Code language: PHP (php)

    A static analysis tool like PHPStan cannot know that per_page is a valid request param defined in your schema. $request['per_page'] could be anything and thus $per_page will be treated as having a mixed type.

    You could solve this with an inline @var tag:

    /**
     * @var int $per_page
     */
    $per_page = ! empty( $request['per_page'] ) ? $request['per_page'] : 100;Code language: PHP (php)

    However, this should only be used as a last resort as it quickly leads to a lot of repetition and the annotations can easily become outdated.

    Luckily, the WordPress stubs package provides a more correct way to describe this code, using array shapes:

    /**
     * @param WP_REST_Request $request Full details about the request.
     * @return WP_REST_Response|WP_Error Response object on success, WP_Error object on failure.
     *
     * @phpstan-param WP_REST_Request<array{post?: int, orderby?: string}> $request
     */
    public function create_item( $request ) {
    	// This is an int.
    	$parent_post = ! empty( $request['post'] ) ? $request['post'] : null;
    
    	// This is a string.
    	$orderby = ! empty( $request['orderby'] ) ? $request['orderby'] : 'date';
    }Code language: PHP (php)

    Using PHPStan for tests

    So far I have focused on using PHPStan for analyzing your plugin’s main codebase. But why stop there? Static analysis is also tremendously helpful for finding issues with your tests and assertions therein.

    To set this up, you will need to install the PHPStan PHPUnit extension and rules, as well as the WordPress core test suite stubs so PHPStan knows about all the classes and functions coming from there, like all the factory methods.

    composer require --dev phpstan/phpstan-phpunit php-stubs/wordpress-tests-stubsCode language: JavaScript (javascript)

    Note: this assumes you are already using phpstan/extension-installer.

    To add the new stubs, amend the configuration file like so:

    parameters:
      level: 1
      paths:
        - src/
      scanFiles:
        - vendor/php-stubs/wordpress-tests-stubs/wordpress-tests-stubs.phpCode language: YAML (yaml)

    With this, you are all set.

    Again, I find running PHPStan on tests very useful. It helps write better assertions, find flawed tests, and uncover room for improvement in your main codebase.

    Running PHPStan on GitHub Actions

    Once you have set up everything locally, configuring the static analysis to run on a Continuous Integration (CI) service like GitHub Actions becomes a breeze. You don’t need to configure a custom action or anything. Simply setting up PHP and installing Composer dependencies is enough. Here’s an example workflow:

    name: Run PHPStan
    
    on:
        push:
            branches:
                - main
        pull_request:
    
    jobs:
        phpstan:
            name: Static Analysis
            runs-on: ubuntu-latest
            steps:
                - name: Checkout
                  uses: actions/checkout@v4
    
                - name: Setup PHP
                  uses: shivammathur/setup-php@v2
                  with:
                      php-version: 'latest'
                      coverage: none
                      tools: composer, cs2pr
    
                - name: Install PHP dependencies
                  uses: ramsey/composer-install@v2
                  with:
                      composer-options: '--prefer-dist --no-scripts'
    
                - name: PHPStan
                  run: composer phpstanCode language: YAML (yaml)

    Recommended PHPStan Extensions

    So far I have introduced the WordPress and PHPUnit extensions for PHPStan, but there are many more that can be valuable additions to your setup. Remember: with phpstan/extension-installer they will be available to PHPStan automatically, but some of them can be further tweaked via the configuration file.

    • swissspidy/phpstan-no-private
      Detects usage of WordPress functions and classes that are marked as @access private and should not be used outside of core. And yes, I actually wrote this one.
    • phpstan/phpstan-deprecation-rules
      Similar to the above, this extension detects usage of functions and classes that are marked as @deprecated and should no longer be used. Again very useful in a WordPress context.
    • phpstan/phpstan-strict-rules
      The highest level in PHPStan is already quite strict, but if you want even more strictness and type safety, this is the extension you want. It’s also possible to enable only some rules.
    • johnbillion/wp-compat
      Helps verify that your PHP code is compatible with a given version of WordPress. It will warn you for things like using functions from newer versions without an accompanying function_exists() check.

    Wrapping Up

    I hope this post serves as a good introduction to using PHPStan in a WordPress project. There are so many more things I could cover, like how to set the PHP version, the PHPStan Pro offering, or the interactive playground. But I didn’t want to make this even more overwhelming, so maybe I’ll save these tips for another post.

    If you have any questions about PHPStan or want to share your experiences with it, I would love to hear them in the comments!

    Finally, if you end up using PHPStan in your WordPress project, consider donating to either PHPStan itself, or Viktor Szépe who maintains the WordPress extension and stubs.

  • Introducing the Periodic Table of WordPress Plugins

    Introducing the Periodic Table of WordPress Plugins

    Today, I am announcing the Periodic Table of WordPress Plugins, a small project of mine to celebrate the 20th anniversary of WordPress and the thousands of plugins that make it special. It does so by displaying the most popular WordPress plugins in a unique way. Before I tell you more about it, here it is in its full glory:

    High resolution screenshot of the periodic table of WordPress plugins in a light theme

    Over 10 years ago I first stumbled upon the Periodic Table of Drupal Modules by Amazee Labs. It showed the most popular Drupal modules in a layout that you might remember from chemistry class. That work inspired me to create a similar version for WordPress plugins, but with a twist — plugintable.com was born.

    It was meant as a showcase of WordPress plugins that automatically updates itself daily. While it was web-only, I’ve always thought about doing a print version as well. You can see this in the archived version depicted below (notice the poster note in the blue header?).

    A screenshot of plugintable.com from January 2013, showing the original periodic table of WordPress plugins.
    A screenshot of plugintable.com from January 2013.

    The site was dynamic from the beginning. It regularly fetched the most popular plugins from the WordPress.org plugin directory and ranked them by the number of active installations. Then, it generated symbol names for each “element”. The plugins were then displayed in a “table” using CSS floats and jQuery 1.9.0. Clicking on each plugin revealed further information such as the description and author name.

    Showcasing the Most Popular WordPress Plugins

    Over the past few months I have rediscovered and resurrected this old project of mine. I figured it would be a cool thing to share for the 20th anniversary of WordPress. After all, WordPress turns 20 on May 27, 2023! 🎉

    So today, I am re-introducing The Periodic Table of WordPress Plugins. Complete with a modernized look & feel, more curation and more useful information than before. And even a poster version!

    A lot has happened in the past decade. For example, the number of WordPress plugins has tripled from 20,000 to over 60,000! It’s great to see many new rising stars among all these plugins. However, the top contenders remain largely unchanged with names like Akismet, Jetpack, and Contact Form 7. Here are some of my observations looking at the current list:

    • Yoast SEO is the #1 most popular WordPress plugin according to publicly available information (active installs, downloads, ratings).
    • Really Simple SSL is the only plugin with 5+ million active installs and a solid 5.0/5.0 rating. Impressive!
    • In the top 20, Site Kit by Google — first released in October 2019 — is the youngest “element” with 3+ million active installs in a little over 3 years.
    • XML Sitemaps is the oldest plugin in the list with its first release in June 2005.
    • Among the most popular plugins, WooCommerce Shipping & Tax has the worst rating with 900k+ active installs and only 2 out of 5 stars.

    Grab your Physical Copy Today

    Did I already mention the periodic table of WordPress plugins is also available as a unique poster? I finally made my dream come true with the help of a print-on-demand provider and Shopify, so that anyone who wants to hang this on their wall can now do so!

    It is available in multiple sizes, with or without frame, and even in a dark mode option! Check out all options in the shop →.

    First and foremost this poster is meant as a nice wall decoration for WordPress enthusiasts like me. The little money this generates will be donated straight back to the WordPress community, so it’s 100% not for profit. Please help me spread the word and consider getting a copy yourself!

    If you prefer printing the poster yourself or perhaps want to use it as a desktop wallpaper on your computer, you can also download the files here:

    Feedback Welcome

    In the future, I will write more about how I built the site, why I chose Shopify over WooCommerce, and my experience selling physical goods online for the first time.

    For now, I am interested in your feedback. Please leave comments or send me an email with any thoughts and questions you might have. Thank you!

  • I18N Improvements in WordPress 6.2

    I18N Improvements in WordPress 6.2

    I just posted a short summary over at make.wordpress.org of all the internationalization (i18n) enhancements and bug fixes in the upcoming WordPress 6.2 release, many of which I worked on myself. Check it out:

  • Client-Side Video Optimization

    Client-Side Video Optimization

    With Web Stories for WordPress, we want to make it easy and fun to create beautiful, immersive stories on the web. Videos contribute a large part to the immersive experience of the story format. Thus, we wanted to streamline the process of adding videos to stories as much as possible. For instance, the Web Stories editor automatically creates poster images for all videos to improve the user experience and accessibility for viewers. One key feature we recently introduced in this area is client-side video optimization — video transcoding and compression directly in the browser.

    New to Web Stories? You can learn more about this exciting format in my recent lightning talk.

    The Problem With Self-Hosting Videos

    The Web Stories format does not currently support embedding videos from platforms like YouTube, which means one has to host videos themselves if they want to use any. And here’s where things get cumbersome, because you have to ensure the videos are in the correct file format, have the right dimensions and low file size to reduce bandwidth costs* and improve download speed.

    A typical use case for a story creator is to record a video on their iPhone and upload it straight to WordPress for use in their next story. There’s just one problem: your iPhone records videos in the .mov format, which is not supported by most browsers. Once you realize that, you might find some online service to convert the .mov file into an .mp4 file. But that doesn’t address the video dimensions and file size concerns. So you try to find another online service or tutorial to help with that. Ugh.

    We wanted to prevent you from having to go down the rabbit hole of figuring this all out.

    * Aside: To reduce bandwidth costs, we are actually working on a solution to serve story videos directly from the Google CDN, which is pretty cool and will help a lot to reduce costs for creators!

    Alternatives

    Of course, there are some alternatives to this. For example services like Transcoder or Jetpack video hosting. These solutions will transcode videos on-the-fly during upload on their powerful servers. So you upload your .mov file, but you receive an optimized .mp4 video. However, that requires you to install yet another plugin. Plus, these services won’t optimize the video to the dimensions optimal for stories. So there’s still room for improvement.

    We wanted a solution without having to rely on third-party plugins or services. Something that’s built into the Web Stories plugin and ready to go, requiring zero setup. And since hosting providers don’t typically offer any tools for server-side video optimization, we had to resort to the client.

    Making Video Optimization Seamless

    In our research, we quickly stumbled upon ffmpeg.wasm, a WebAssembly port of the powerful FFmpeg program, which enables video transcoding and compression right in the browser. Jonathan Harris and I did some extensive testing and prototyping with it until we were comfortable with the results.

    The initial prototype was followed by multiple rounds of UX reviews and massive changes to media uploads in the Web Stories editor. In fact, I basically rewrote most of the upload logic so we could better cater for all possible edge cases and ensure consistent user experience regardless of what kind of files users try to upload.

    The result is super smooth: just drop a video file into the editor and it will instantly get transcoded, compressed and ultimately uploaded to WordPress. Here’s a quick demo:

    Client-side video optimization in the Web Stories editor in action

    Technical Challenges

    FFmpeg Configuration

    A lot of our time fine-tuning the initial prototype was spent improving the FFmpeg configuration options. As you might know, there’s a ton of them and you can easily shoot yourself in the foot if you’re not familiar with them (which I personally wasn’t). We tried to find the sweet spot with the best tradeoff between video quality, encoding speed, and CPU consumption.

    The FFmpeg options we currently use:

    OptionDescription
    -vcodec libx264Use H.264 video codec.
    -vf scale='min(720,iw)':'min(1080,ih)':
    'force_original_aspect_ratio=decrease',
    pad='width=ceil(iw/2)*2:height=ceil(ih/2)*2'
    Scale down (never up) dimensions to enforce maximum video dimensions of 1080×720 as per the Web Stories recommendations, while avoiding stretching.

    Adds 1px pad to width/height if they’re not divisible by 2, to prevent FFmpeg from crashing due to odd numbers.
    -pix_fmt yuv420pSimpler color profile with best cross-player support
    -preset fastUse the fast encoding preset (i.e. a collection of options).

    In our testing, veryfast didn’t work with ffmpeg.wasm in the browser; there were constant crashes.
    FFmpeg configuration used in the Web Stories WordPress plugin

    Cross-Origin Isolation

    ffmpeg.wasm uses WebAssembly threads and thus requires SharedArrayBuffer support. For security reasons (remember Spectre?), Chrome and Firefox require so-called cross-origin isolation for SharedArrayBuffer to be available.

    To opt in to a cross-origin isolated state, one needs to send the following HTTP headers on the main document:

    Cross-Origin-Embedder-Policy: require-corp
    Cross-Origin-Opener-Policy: same-originCode language: HTTP (http)

    These headers instruct the browser to block loading of resources which haven’t opted into being loaded by cross-origin documents, and prevent cross-origin windows from directly interacting with your document. This also means those resources being loaded cross-origin require opt-ins.

    You can determine whether a web page is in a cross-origin isolated state by examining self.crossOriginIsolated.

    In addition to setting these headers, one also has to ensure that all external resources on the page are loaded with Cross Origin Resource Policy or Cross Origin Resource Sharing HTTP headers. This usually means having to use the crossorigin HTML attribute (e.g. <img src="***" crossorigin>) and ensuring the resource sends Access-Control-Allow-Origin: * headers.

    Now, if you have full control over your website, setting up cross-origin isolation is relatively easy. But the Web Stories editor runs on someone else’s WordPress site, with all sorts of plugins and server configurations at play, where we only control a small piece of it. Given these unknowns, it was not clear whether we could actually use cross-origin isolation in practice.

    Luckily, Jonny was able to implement cross-origin isolation in WordPress admin by output buffering the whole page and adding crossorigin attributes to all images, styles, scripts, and iframes if they were served from a different host.

    This won’t catch resources that are loaded later on using JavaScript, but that’s quite rare in our experience so far. And since we only do this on the editor screen and only when video optimization is enabled, there are less likely to be conflicts with other plugins.

    Other Use Cases

    Over time, we have expanded our usage of FFmpeg in the Web Stories editor beyond mere video optimization during upload. For example, users can now optimize existing videos as well and we also use it to quickly generate a poster image if the browser is unable to do so. But there are two other clever uses cases that I’d like to highlight:

    Converting Animated GIFs to Videos

    Did you know that the GIF image format is really bad? Animated GIFs can be massive in file size. Replacing them with actual videos is better in every way possible. So we tasked ourselves to do exactly this: convert animated GIFs to videos.

    Today, in Web Stories for WordPress, if you upload a GIF, we detect whether it’s animated and silently convert it to a much smaller MP4 video. To the creator and the users viewing the story, this is completely visible. It still behaves like a GIF, but it’s actually a video under the hood. Instead of dozens of MB in size, the video is only a few KB, helping a lot with performance.

    This feature was actually inspired by this issue my colleague Paul Bakaus filed for Gutenberg. It would be super cool to have this same feature in the block editor as well.

    Muting Videos

    Often times, creators upload videos to their stories that they want to use as a non-obtrusive background. For such cases, they’d like the video to be muted. But just adding the muted attribute on a <video> still sends the audio track over the wire, which is wasteful.

    For this reason, when muting a video in the story editor, we actually remove any audio tracks behind the scenes. It’s one of the fastest applications of FFmpeg in our code base because the video is otherwise left untouched. So it usually takes only a few seconds.

    What’s Next?

    I am really glad we were able to solve cumbersome real-world issues for our users in such a smooth way. Even though it’s quite robust already, we’re still working on refining it and expanding it to other parts of the plugin. For example, we want to give users an option to trim their videos directly in the browser.

    We can then use our learnings to bring this solution to other areas too. For example, it would be amazing to land this in Gutenberg so millions of WordPress users could take advantage of client-side video optimization. However, implementing it at this scale would be inherently more complex.

  • Improving WordPress Internationalization with ESLint

    Improving WordPress Internationalization with ESLint

    Avid readers will already know that I am very passionate about internationalization (I18N). Some of my most popular blog posts are about that topic:

    Internationalization is an important aspect in WordPress development as it lays the foundation for a project’s global success. Unfortunately, it is often done wrong, but things get better over time thanks to simplified APIs, improved documentation, and tooling. For example, the WordPress Coding Standards for PHP_CodeSniffer has been detecting incorrect usage of I18N functions for years now. However, there was no equivalent for this kind of detection in JavaScript source files — until today.

    Being involved with the development of many JavaScript-heavy WordPress projects, I often see common mistakes when using the @wordpress/i18n package that could be easily caught by some kind of linter. To validate my thinking, I set out to fix this issue and contribute the solution to the WordPress community.

    Extending The WordPress ESLint Plugin

    First, I started writing down all the things that could possibly be developed to help improve the WordPress JavaScript I18N landscape. This includes things like detecting wrong usage of text domains, missing translator comments, and flagging usage of variables in translatable strings. I even thought about detecting strings that should probably be translatable, but currently aren’t. Tricky to do, but one can dream.

    Then, I was looking for the best place to implement this. Luckily, WordPress and also our own projects already use a handy tool for this: ESLint. ESLint is the JavaScript-equivalent of PHPCS, and the @wordpress/eslint-plugin package is the one that can be used to enforce WordPress coding standards. For me, that was the perfect place to start.

    By reading through ESLint’s great developer documentation I learned all about creating custom linter rules, and studying existing rules in the aforementioned package, as well as eslint-plugin-wpcalypso from WordPress.com, hel.

    Before I knew it, I was knee-deep in writing ESLint rules, tests, and fixes for the issues my rules discovered. Hundreds of lines of code later, you can now use these new features in your projects!

    The New I18N ESLint Ruleset

    In total, I ended up creating six new ESLint rules around internationalization, and improving one existing rule. If you’re already using the recommended ruleset from the WordPress ESLint plugin (version 5.0.0 or higher!), you automatically benefit from these enhancements. Alternatively, you can also only extend the I18N ruleset if wanted. For example:

    {
    	"extends": [ "plugin:@wordpress/eslint-plugin/i18n" ]
    }Code language: JSON / JSON with Comments (json)

    It includes the following rules:

    @wordpress/i18n-text-domain

    The 18n-text-domain rule enforces passing valid text domains to translation functions (e.g. only string literals). It flags things like __( 'Hello World' ), but allows __( 'Hello World', 'awesome-sauce' ) if your project’s text domain is awesome-sauce.

    Your desired project text domain can be specified in the ESLint config as follows:

    {
    	"@wordpress/i18n-text-domain": [ "error", {
     		"allowedTextDomain":  "awesome-sauce"
    	} ]
    }Code language: JSON / JSON with Comments (json)

    @wordpress/i18n-translator-comments

    If using translation functions with placeholders in them, they should have accompanying translator comments. The i18n-translator-comments rule flags the lack thereof.

    @wordpress/i18n-no-variables

    In WordPress development, you must call translation functions with valid string literals as arguments. They cannot be variables or functions for technical reasons. Use the i18n-no-variables rule to easily enforce this.

    @wordpress/i18n-no-placeholders-only

    Translatable strings that consist of nothing but a placeholder, e.g. __( '%s' ), cannot be translated. The i18n-no-placeholders-only rule prevents such usage.

    @wordpress/i18n-no-collapsible-whitespace

    With the i18n-no-collapsible-whitespace rule you can prevent using complex whitespace in translatable strings. Relying on HTML to collapse such whitespace can make translation more difficult and lead to unnecessary retranslation.

    @wordpress/i18n-ellipsis

    Lastly, the i18n-ellipsis rule disallows using three dots in translatable strings. Three dots for indicating an ellipsis should be replaced with the UTF-8 character (horizontal ellipsis, U+2026) as it has a more semantic meaning.

    @wordpress/valid-sprintf

    The existing valid-sprintf rule enforces valid usage of the sprintf function exposed by the @wordpress/i18n package. I’ve extended it to catch and prevent a mix of ordered and non-ordered placeholders. Multiple sprintf placeholders should be ordered so that strings can be better translated.

  • Saving the Romansh Language with WordPress

    Saving the Romansh Language with WordPress

    Tgi che sa rumantsch sa dapli — if you know Romansh, you know more

    (Deutsche Version)

    Switzerland has four official languages: German, Italian, French, and Romansh. Growing up in the canton of Grisons, I got in touch with the latter early on. Unfortunately, it is a dying language. To do something against this, I decided to translate WordPress into Romansh. And I don’t even speak the language!

    But WordPress would be the ideal platform for a Romansh translation. The world’s most popular content management system (CMS) has a market share of 35% and is also very common in Switzerland. That means many people are interacting with it on a daily basis.

    It all began with a simple idea a couple of years ago, I think it was around WordCamp Europe 2015. After talking about this with some people, many showed interest and also thought it would be a cool idea. However, nothing concrete happened yet.

    The First Steps

    In order to move things forward, I got in touch with the WordPress Polyglots team to properly set up Rumantsch on the translation management platform. I figured that this was the biggest hurdle to overcome. Once the translation platform was ready, interested people could just start translating and actually make this happen. I was able to do some basic translations myself thanks to an online dictionary. However, for the more complex strings I needed help from people who actually speak the language.

    Besides talking to friends and acquaintances who speak Romansh, I also got to know Gion-Andri Cantieni and his initiative Software rumantscha. I was pretty impressed when I learned that they have been successfully translating Firefox, Microsoft Office, and even the Contao CMS to Romansh for quite some time. This was even in the news, which showed me that it’s not a crazy idea at all to try to translate WordPress.

    Now that we were a group of people, we were quickly able to translate about a third of WordPress to Rumantsch. At WordCamp Europe 2017, I shared the story about how we got there with the global WordPress community:

    Getting Involved

    Efforts stagnated a bit after that, but now I want to take another attempt at translating WordPress into the Romansh language. It’s quite fitting that this year marks the 100-year anniversary of Lia Rumantscha, the local institution that promotes the Romansh language and culture.

    https://www.grheute.ch/2018/07/19/die-lia-rumantscha-wird-hundert-und-feiert-zuoz/

    As of today, the Rumantsch translation of WordPress is around 35% complete. This is what it looks like in the WordPress admin:

    WordPress in Rumantsch

    To get it to 100%, I need your help!

    First of all, if you’re interested in using WordPress in Rumantsch or want to support the translation efforts in any form, please let me know!

    If you want to jump right into the action and start translating WordPress, all you need is a WordPress.org user account. Once signed up you can head to translate.wordpress.org right away to find all the projects that can be translated.

    This includes WordPress core, but also the WordPress.org websites and even the WordPress mobile apps. The most important project to translate is certainly WordPress 5.0, the current WordPress release.

    We’ve collected some helpful resources for translators at roh.wordpress.org/translatar. Yes, that’s right — WordPress en Rumantsch has its own website! In addition to that page, the Polyglots handbook has some very useful information as well.

    Also make sure to join the WordPress Switzerland Slack workspace at wpch.slack.com using your WordPress.org email address (<username>@chat.wordpress.org). There we have a dedicated #polyglots channel for this purpose.

    Have you got any questions so far? Please leave a comment, send an e-mail, or ping me on Twitter.

  • Get Ready for WordCamp Zurich 2019

    Get Ready for WordCamp Zurich 2019

    It’s been a while since I have been involved with organizing a WordCamp. After a 4-year break, this is changing now. I am very excited that WordCamp Zurich is taking place on September 14, 2019.

    Back in 2014 and 2015, we already hosted two amazing WordPress conferences in Zurich. After a small local event in Switzerland in 2011, those were the first bigger WordCamps with a more international audience. We called them WordCamp Switzerland, as the Swiss community felt we were to small to host city-named events.

    In the following years, many things have changed. The Swiss WordPress community was — and still is — flourishing. This year’s WordCamp Europe in Berlin is excellent proof for that, as there were about 30 attendees from Switzerland present, which I think is amazing.

    The Swiss WordPress community at WordCamp Europe 2019 in Berlin, Germany (Photo: Florian Ziegler)

    Many new WordPress meetups throughout the country have been started since the last WordCamp Switzerland. There have been WordCamps in Geneva, Bern, and Lausanne in the last three years. As a community, we thought now is a good time to go back to Zurich for once.

    What to Expect at WordCamp Zurich

    WordCamp Zurich is not just a local WordPress conference. It is a collaboration between WordPress enthusiasts and friends from all over Switzerland, joining forces to make sure this event will be just as awesome as the previous conferences.

    Conference Day

    The main event is taking place on Saturday, September 14th in the heart of Zurich. This is gonna be one full day with talks from speakers from both local and international speakers, in German and English.

    The Call for Speakers for this conference day is going to open soon, so make sure to subscribe to any news updates.

    Contributor Day

    On September 13, the day before the actual conference, we are organizing a so-called Contributor Day. This event gives you a special opportunity to learn more about how you can contribute to the WordPress open source project.

    This will be a smaller gathering at a different venue. Once registration is open, we will communicate it on the WordCamp website and all social media channels.

    Call for Sponsors

    To make this event a success, the Swiss WordPress community needs your support! WordCamps are non-profit events, organized by people from within the community on a voluntary basis. We rely on companies and individual sponsors to support us, so that we are able to provide attendees with a great event at very affordable ticket prices.

    If you are interested in sponsoring WordCamp Zurich, please check out our Call for Sponsors post.

  • An Introduction to WP-CLI

    An Introduction to WP-CLI

    For the last two years I have been heavily contributing to WP-CLI. WP-CLI is the official command line tool for interacting with and managing WordPress sites. Especially through my work on the wp i18n command, which provides internationalization tools for WordPress projects, I learned more about how people interact with WP-CLI and command line tools in general. With this introductory blog post I intend to show you how easy it can be to use WP-CLI.

    Disclaimer: this post is basically the written version of my talk at this year’s WordCamp London. The recorded video should be available soon.

    The Command Line

    Before we dive right into WP-CLI, I want to introduce you to some general command line basics. This way you can get a better picture of how command line tools are meant to work and why they might respond in a certain way.

    Simply put, the command line is a text interface to interact with a computer. Before we had all these graphical user interfaces, the command line prompt is basically the only thing you got when booting up your computer. There you could type in some command that would execute a certain program.

    Nowadays the shiny UIs on our computers hide all the complexity underneath. However, the command line still gives you a powerful way to do basically anything on your computer. A big benefit of command line tools is that you can easily automate and even combine them together.

    On your computer, you can access the command line using a terminal application. It looks a bit like this:

    The terminal with its command line prompt.

    What you’ll usually see in the terminal is the command line prompt in the form of the $ (dollar) sign. That’s where you can then enter the name of the application that you want to run — in this case myprogram — and some arguments that should be passed to the program. After that, magic stuff will happen 🙂

    $ myprogram --foo="bar" --debug names.txtCode language: JavaScript (javascript)

    In all the upcoming examples, you’ll see this $ at the beginning of each line, indicating the command prompt where you would type the commands.

    There are essentially three types of command line arguments: named arguments, flags, and positional arguments. Here, we set the value of foo to bar. This is a named argument. Flags are like on/off switches. So passing --debug here would turn on debug mode. Last, names.txt is just a name of a file that we want to pass to the program. You could have many more of these so called positional arguments.

    Let’s use a more real-life example! Here’s how you could update literally all of the WordPress sites you manage using three simple commands offered by WP-CLI:

    # Update all your WordPress sites at once
    $ wp @all core update
    $ wp @all plugin update --all
    $ wp @all theme update --allCode language: PHP (php)

    Of course you could further tweak this. For example if you only want to do minor updates from let’s say WordPress 5.2 to 5.2.1 instead of 5.3. That would just require you to type a few more letters.

    Just imagine how long it would take you to update all your sites by manually clicking on some buttons.

    Command Line Building Blocks

    While WP-CLI is a command that you first need to install, there are already plenty of commands available on your system. Here’s a short list of some more common ones:

    • List directory contents: ls
    • Print working directory: pwd
    • Change directory: cd
    • Remove files: rm
    • Make new directory: mkdir

    There are tons of these little commands. And as you might notice from this list here, these commands all have a very simple job to do. Creating such small programs is actually one of the Unix philosophies: write programs that do one thing and one thing only, and write programs that work together.

    I often like to compare them to Lego bricks. A single command only gets you so far. However, when you combine them together, you can build some pretty cool stuff!

    In this post I only cover the basics of the command line. To learn more about it, I suggest checking out resources like Codecademy tutorials or perhaps LinuxCommand.org.

    Exit Codes

    Something that might be perceived as odd at first is that some commands don’t return anything. At first glance, you might think that they don’t work, since nothing is happening. One example for that is the WP-CLI command to check if your WordPress site has already been installed or if you have yet to set it up: wp core is-installed. Although nothing is being output to you directly, the command’s exit code will tell you the site’s installation status.

    To quickly see the exit code of your previously run command, you can use the dollar question mark variable:

    $ wp core is-installed
    $ echo $?
    0Code language: PHP (php)

    It’s good to know that every command has an exit code. On POSIX systems, an exit code of 0 means everything is OK (success), whereas any number from 1 to 255 is a non-success (or error, if you so will). Some commands only use 0 and 1 as exit code though, as they don’t have for more.

    Most of the time, you won’t need that $? variable to find out the exit code, as it’s mostly useful in combination with other commands.

    Command Chaining

    A very simple such combination would be command chaining. For example, instead of running commands like mkdir and cd on their own, you can write things like mkdir test && cd test to say “create a new directory and when that is successful, switch to that directory”. Or the other way around: “create a new directory or print a nice error message when something goes wrong” could be written as mkdir test || echo "Oops".

    So these && and || operators actually check for these exit codes:

    $ wp core is-installed && wp core update
    $ wp post exists 123 || echo "Post does not exist"Code language: PHP (php)

    Pipes

    Another way of combining programs are pipes, or pipelines. Simply put, pipes let you use the output of a program as the input of another one. Here are a few examples:

    • Filter lists using regex: ls -l | grep ".php$"
    • Get the word count: echo "These are four words" | wc -w
    • Delete temporary files: find . -name '*.tmp' | xargs rm

    The one command I like most there is the last one, xargs. In that example, the find command returns a list of all temporary files that one might want to clean up. xargs then takes this list and runs the rm command on each of the files to delete them individually.

    Here’s how you could use xargs in combination with WP-CLI:

    $ wp site list --field=url | xargs -n1 -I % wp --url=% option update my_option my_valueCode language: PHP (php)

    This will retrieve a list of all sites in a network, and then for each of the sites it adds a specific option to the database. Some more examples with WP-CLI and xargs can be found in the handbook. That is also a great place to look up the exact arguments needed for xargs.

    Scripting

    Many times, you need to run multiple commands in a row or run them very often. To make this easier, you can create a shell script for these tasks.

    A shell script is basically a text file with one or more commands that are executed in a linear order. You can also add some code comments to the script to make it easier to comprehend. Here’s a simple example:

    #!/bin/bash
    
    # Update all WordPress sites at once
    
    echo "Start updates..."
    wp @all core update
    wp @all plugin update --all
    wp @all theme update --all
    
    echo "Finished!"Code language: PHP (php)

    Now, we can just execute this single script instead of having to type all commands manually every time we need to use it:

    $ my-first-script.sh
    
    Start updates...
    # [...]
    Finished!Code language: PHP (php)

    This also makes it very easy to share, drop on a server, put on GitHub for collaboration, and so on.

    Meet WP-CLI

    With these fundamentals set, let’s add some WordPress to the mix and see what we can do with WP-CLI.

    First of all, many web hosts nowadays install WP-CLI on all of their servers by default. That means it is immediately available and you don’t have to worry about installing it first and setting everything up.

    Second, it is very intuitive to use and has extensive documentation for all the available commands and configuring WP-CLI. This way you can get started quickly, even if you are not a developer.

    Finally, the goal of WP-CLI is to provide the fastest way to perform any task in WordPress. So if you are ever in doubt about how to do something in WordPress, you might want to check the command line first.

    Getting Started

    To get started with WP-CLI, open the built-in documentation using wp help. This will give you a general help screen with a list of commands. You can also get a help screen for a specific command, e.g. by typing wp help post.

    For information about your WP-CLI environment, you can use wp cli info. And if that command tells you that your version of WP-CLI is out of date, you can simply update it using wp cli update.

    Bundled Commands

    When you install WP-CLI, it comes with a long list of useful commands. These are already built-in and cover pretty much all aspects of WordPress. You can manage things like posts, comments, and plugins all through the command line.

    But there are also some commands that don’t actually require WordPress, because they work independently of a specific WordPress site. One such command is wp i18n, which I’ve mentioned at the beginning of this article.

    Note: You can learn more about the wp i18n command in my blog post about internationalization in WordPress 5.0.

    To give you an idea of what you can do with WP-CLI, here’s a list of some more or less common examples:

    • Delete all products: wp post delete $(wp post list --post_type='product' --format=ids)
    • Generate some dummy users for testing: wp user generate --count=500
    • Show all active plugins: wp plugin list --status=active
    • Perform a search and replace operation on the database: wp search-replace 'http://example.test' 'http://example.com' --skip-columns=guid
    • Generate translation files: wp i18n make-pot

    Global Parameters

    There are some arguments that you can pass to all commands offered by WP-CLI. For example, if you like its output to be more quiet, you can suppress some of the informational messages WP-CLI usually prints using --quiet. Or for the other way around, you can use the --debug flag to get a little more extra information.

    Super helpful is also the ability to skip some plugins or even themes using --skip-plugins and --skip-themes. Some plugins might not always work well in combination with WP-CLI. This flag this allows you to disable a plugin or theme for just this one command.

    Learn more about global parameters.

    Common Use Cases

    Install a WordPress Site

    WordPress praises itself for its famous 5 minutes installation procedure. The truth is, it’s often a bit longer than that. But with WP-CLI, we can actually bring this time down to seconds.

    Using just three WP-CLI commands we can download WordPress, set up wp-config.php and run the whole installation procedure, without even having to open a browser.

    $ wp core download --locale=en_GB
    $ wp core config --dbname=mynewsite --dbuser=root
    $ wp core install --url=mynewsite.dev --title="My Site"Code language: JavaScript (javascript)

    Using the third-party wp login command you could even generate a link that automatically logs you in afterwards.

    Perform all Updates on a Site

    This is similar to the example I gave earlier, but now just for a single site:

    $ wp core update
    $ wp plugin update --all
    $ wp theme update --all

    Regenerate Thumbnails

    Another useful command that comes in handy when changing image sizes is wp media regenerate. There’s no need to install a plugin for this and performing this tedious task in the browser. With WP-CLI you can do it all on the command line and let it run in the background. You can even automate it using a cron job.

    $ wp media regenerate --yes --only-missing

    Site Migrations

    WP-CLI is also an ideal tool for site migrations. You can not only export and import your database for an easy backup of your site. You can also use it when changing your domain name or when going from HTTP to HTTPS.

    $ wp db export
    $ wp db import
    $ wp search-replace 'https://old.blog' 'https://new.blog' --skip-columns=guidCode language: JavaScript (javascript)

    Evaluate Code

    This one is more for developers I guess. wp eval allows you to quickly execute some PHP code, which is very useful for debugging. It allows you to quickly find out the value of a variable or run a function. This is especially useful if there is no WP-CLI command for a certain feature yet

    $ wp eval 'echo WP_CONTENT_DIR;'
    /var/www/wordpress/wp-contentCode language: JavaScript (javascript)

    Flush Rewrite Rules

    When you installed some plugins that messed with your permalinks in some way, and now your URLs aren’t working properly anymore, you can simply run wp rewrite flush to clean up and regenerate the permalinks.

    Configuration Files

    Many aspects of WP-CLI can be tweaked through configuration files. WP-CLI looks for these in various locations. This way you can have a global configuration file, as well as per-project configurations. The lookup order is like this:

    1. wp-cli.local.yml
    2. wp-cli.yml
    3. ~/.wp-cli/config.yml

    A simple configuration file could look like this. Here you just tell WP-CLI where your site is located and what the site URL is:

    path: wp-core
    url: https://pascalbirchler.com
    user: Pascal
    disabled_commands:
      - plugin installCode language: JavaScript (javascript)

    I think it’s really cool that it allows you to disable certain commands. This way you can prevent users from running commands that could potentially break your site if not executed with care.

    Aliases

    Configuration files can also contain defaults for any subcommand, as well as aliases to one or more WordPress installs. This way you can run WP-CLI on a server without having to memorize credentials and log into that server first.

    Aliases can even reference other aliases to create alias groups. Using just one alias you can simultaneously run a command against multiple sites on different servers.

    @staging:
      ssh:
      user:
      path:
    @production:
      ssh:
      user:
      path:
    
    @mywebsite:
      - @staging
      - @production

    This way you can use wp @mywebsite <command> to run something across both the staging and production environments of a site.

    Note: WP-CLI automatically creates the @all alias group you’ve seen in previous examples, which allows you to run a command across all your websites.

    Extending WP-CLI

    WP-CLI is very powerful and contains a lot of super helpful commands. However, if in any case these are not enough for you, you can also extend WP-CLI with third-party commands.

    WP-CLI is very modular. All the built-in commands are actually separate packages, and adding more commands just means adding another package to the mix. You could even install a package that overrides one of the built-in commands. Thanks to this modularity, the WordPress community is steadily creating new commands for WP-CLI.

    The commands you need to know:

    • List all installed packages: wp package list
    • Install a new package: wp package install <package>
    • Remove an existing package: wp package uninstall <package>
    • Update installed packages: wp package update

    In these examples, <package> refers to name of the GitHub repository the package is located at, or a fully-qualified URL.

    After adding a new package, you’re all set and you can immediately run it. No need to restart your computer or anything.

    Magic Login Links

    As mentioned above, the wp login command allows you to log into WordPress with secure passwordless magic links. These can be generated on the fly or even sent via email.

    $ wp package install aaemnnosttv/wp-cli-login-command
    $ wp login create <user>Code language: HTML, XML (xml)

    Vulnerability Scanner

    10up created a command that checks your installed plugins and themes against the WordPress vulnerability database. This way you can quickly check whether your site is potentially at risk for getting hacked.

    $ wp package install 10up/wp-vulnerability-scanner
    $ wp vuln status

    Image Optimization

    Another handy command I recently found allows you to do lossless image optimizations on all your media files in WordPress.

    Image optimization is resource and time intensive just by its nature. It makes sense to run this on the server at a convenient time. Plus, this way you don’t have to install the same image optimization plugin on all of your WordPress sites.

    $ wp package install typisttech/image-optimize-command
    $ wp media regenerate
    $ wp image-optimize batch

    Write Your Own Custom Command

    Of course, you can also create your very own WP-CLI command … using nothing less than WP-CLI itself!

    WP-CLI ships with commands to scaffold new plugins, themes, Gutenberg blocks, and even WP-CLI commands. All scaffolded commands will contain proper documentation, some initial boilerplate code, and even the complete testing setup.

    What’s Currently Missing?

    Need some inspiration for your first WP-CLI command? I recommend checking out the project’s ideas repository where people can suggest new features. Currently high on the list are commands for the built-in privacy management tools, as well as commands related to Gutenberg.

    Once you have found something you want to create a command for, you can use the powerful wp scaffold package command to bootstrap your new package. Yes, that’s a command to create another command! 🤯

    Note: The scaffold command is not yet fully updated for the new WP-CLI 2.0 infrastructure, so it currently is also worth checking out other existing commands like wp maintenance-mode to see how they’re constructed.

    Further Reading

    Wanna learn more about WP-CLI? I recommend checking out the project’s blog and handbook on make.wordpress.org/cli. There’s also a #cli Slack channel where you can ask questions and contribute back to WP-CLI. And of course, all the code of WP-CLI can be found on GitHub.


    Many thanks to Alain Schlesser for maintaining WP-CLI and striving to make it easier for people to use WP-CLI. His excellent presentation at WordCamp Berlin 2017 served as an inspiration for this post.

    Thanks to Alain and John Blackbourn for proofreading this post and giving valuable feedback.

  • CMS Security Summit

    A couple of weeks ago, I had the opportunity to attend the CMS Security Summit in Chicago. For this event, Google brought together content management systems, security researchers, and hosting providers to talk about security. WordPress, powering a third of the web, was represented by security team lead Barry.

    As a WordPress core committer and Noogler, this was a very insightful event for me. All the discussions with the attendees were super valuable—just the temperatures were a bit cold for my taste (-50 degrees, yikes!). If you wanna learn more about the event, some people published recap blog posts:

    I think the key takeaway is that most projects are dealing with the same issues and that they all benefit from working more closely together. Some examples include:

    • Automatic updates and package signing
    • Code reviews and static analysis
    • Collaborating with security researchers

    For this blog post, I want to dig a bit deeper on code analysis and what it means for WordPress.

    Static Code Analysis for WordPress Plugins

    WordPress is only as strong and secure as its ecosystem. Part of that ecosystem are the 60,000 plugins and themes that are available for download on WordPress.org. It’s impossible to manually scan all these projects for potential security vulnerabilities.

    At the summit, the RIPS code analysis platform was mentioned a few times. It’s a paid solution, but they also work together with open source projects. For example, Joomla uses RIPS to continuously scan their code base. At the moment WordPress doesn’t use that tool, but for RIPS the platform is of interest either way. The just recently demonstrated this via their WordPress Security Advent Calendar.

    Another example is their security risk analysis platform, CodeRisk. According to the website, CodeRisk “rates the security risk of WordPress plugins by analyzing them with the RIPS static code analyzer and combining the results into an easy to understand value”.

    I’m not sure how useful a plain number is, but I guess it works well for marketing. Anyway, I wanted to give the site a try to find out if there’s more behind that. It turns out that as a plugin developer you get free access to their static code analysis tool to scan all your plugins for security vulnerabilities.

    This is a really nice gesture! I wondered if other people use that feature too, so I posted a quick poll on Twitter:

    In that poll nobody said they use the CodeRisk platform, which was a bit of a surprise to me. Perhaps it’s not clear enough what the site does, or it’s just too complicated to set things up.

    Tools like this demonstrate that there are lots of possibilities to improve security in the wider WordPress ecosystem and in the overall CMS landscape. I’m curious to see how this area evolves in the next few years.