Dr Fragen in the operating room


  • Don’t forget to save any new path commands into .zshrc so the terminal session persists.

    1. Open the Terminal app
    2. Install homebrew
    3. You might be asked if you want to install the Xcode Command Line Tools, confirm.
    4. Install git by typing brew install git
    5. Install Docker on Apple Silicon
    6. Install node via brew install node@16 or brew install node@18, your preference.
      • I install node via nvm, but it’s not necessary
      • Install Node by using nvm ( I use node 16.x at the moment )
        • Open a terminal session
        • brew install nvm
        • Run nvm install 16
        • Run nvm use 16
        • Run nvm alias default 16, so every shell session uses this version of node by default
    7. Install npm via brew install npm@6
      • Switch to npm v6.x via npm install npm@6 -g
      • Make sure you copy path commands to your .zshrc file
    8. Clone the GitHub repository with the wordpress-develop environment.
      • Open a terminal session
      • cd ~
      • git clone https://github.com/WordPress/wordpress-develop.git
      • cd ./wordpress-develop
      • export DOCKER_DEFAULT_PLATFORM=linux/amd64
    9. Open the Docker app in your computer and ignore the welcome screen that tells you to create a container
    10. Now type these commands in the terminal
      • npm install
      • npm run build:dev
      • npm run env:start
      • npm run env:install
    11. You should be able to access the development version of WordPress at localhost:8889. You can access it with username admin and password password
    12. Additional commands
      npm run env:stop
      npm run env:reset



    • Open a terminal session
    • cd ./wordpress-develop
    • export DOCKER_DEFAULT_PLATFORM=linux/amd64
    • Open Docker.app
    • rm -rf node_modules && npm install && npm run build:dev && npm run env:start
    • npm run env:install

    May need to re-run npm run env:install if it stalls or errors at end. This is because MySQL may not be ready yet.


    • npm run env:stop && npm run env:reset && rm -rf node_modules
    • Quit Docker Desktop when done.

    Run individual tests

    • npm run test:php -- --filter Tests_Admin_IncludesWpUpgrader
    • npm run test:php — –group admin

    Don’t commit package-lock.json with your PR to core or you will have issues.

  • The need or desire to use a personal remote management service makes a few assumptions.

    1. It assumes that the developer has personal or client projects that are under version control on a system like GitHub, Bitbucket, GitLab, or some other system.
    2. It assumes that these projects are utilized on multiple sites.
    3. Lastly, it assumes that the developer would like a simple method of pushing updates to these multiple sites.

    GitHub Updater allows for updating of plugins and themes that are developed outside of the official WordPress repository. Often these projects reside in GitHub, Bitbucket, or GitLab. Updating these repositories to a handful of sites usually involves visiting that site’s dashboard and updating the plugin or theme in the usual manner. The difficulty arises when you’re managing the same plugin(s) or theme(s) on many, many sites.

    Current State of Updating

    You don’t want to spend time visiting each site to update these repositories. It takes too long and quickly becomes inconvenient. It’s much simpler to use a remote management system and push updates to multiple sites. There are several commercial plugins and services to accomplish this. Among them are iThemes Sync, ManageWP, MainWP, and InfiniteWP.

    The problem is that these services were designed only to work with the official WordPress repository and not with GitHub, etc. These plugins all work by showing you that an update is pending and allow you to update all the plugins for a site en masse or all of a particular plugin on multiple sites. Set up of these plugins requires a variable degree of effort and occasional errors can be difficult to troubleshoot.

    GitHub Updater has had the ability to utilize many of these remote management services for a couple of years but it’s always been a bit unstable and difficult to test. Recently much of this instability has been removed by allowing for GitHub Updater’s API calls regardless of the web site visitor’s privileges. However, it still requires someone visit the site to ensure that WP-Cron runs and GitHub Updater maintains current update information.

    Webhook Updating

    GitHub Updater has also been able to utilize a webhook to update repositories too. This method doesn’t require WP-Cron or anyone to even visit the site. Send the appropriate webhook and the repository will update. Personally, I use a webhook to update GitHub Updater on the develop branch on my test site every time a GitHub push is identified. This way I can ensure I’m always testing with the most recent commits.

    Updating via a webhook is a more of a push than a pull. For reference, my idea of a pull would be the site shows that an update is pending and pulls the update from the remote source. A push would be similar to a re-installation of the plugin from the current source. In GitHub Updater’s parlance, it’s exactly like branch switching. If you switch to the same branch it’s simply re-installing with the latest code. There is no version checking in this process.

    REST API Endpoint Updating

    GitHub Updater previously used a admin-ajax.php request to provide for webhook updating. I have finally re-written this to an actual REST API endpoint and it has made the Git Remote Updater possible.

    Currently there are 3 endpoints.

    1. repos – returns a listing of the sites GitHub Updater enabled plugins and themes along with their current branch.
    2. update – processes the actual webhook request.
    3. test – Yeah, I just left it in. 馃檭

    Git Remote Updater

    Recently I’ve written a plugin to facilitate your own personal remote management system, Git Remote Updater. Git Remote Updater will allow you to update individual repositories on multiple sites or all the repositories in a single site with a single button click.

    How It Works

    Functionally, the Git Remote Updater Plugin simply sends a webhook as an HTTP GET request to the site. This allows GitHub Updater to update the repository. The best part is that Git Remote Updater is able to send multiple webhook requests with a single click.

    Git Remote Updater only needs the site鈥檚 URL and API key from GitHub Updater to enter into the Git Remote Updater Settings tab.

    GitHub Updater Site Data
    GitHub Updater Site Data
    Git Remote Updater Settings
    Git Remote Updater Settings

    This site specific data is all that is necessary to validate with the site and update the repositories. This data only needs to be updated if the site’s Remote Management API key changes.

    The API key is easily reset and only functions on the update endpoint to authorize an update on the site and one the repos endpoint to collect repository data.

    There is a REST API endpoint that returns the site’s GitHub Updater enabled plugins and themes along with data regarding the installed branch. The second REST API endpoint is for updating. It utilizes data from the JSON file to form a correct webhook for updating. Both of these endpoints require an API key for authentication.

    In order to ensure that this remote updating works, updating must work on the site’s dashboard too. This means that any access tokens or username/password data must be correctly saved on the remote site. I have found that utilizing the Git Remote Updater works best on a local development environment.

    Update by Repository or by Site

    There are 2 ways to update in Git Remote Updater. You may update individual repositories on multiple sites or you may update multiple repositories on an individual site. A dropdown menu conveniently switches between the two options.

    Git Remote Updater by Repository
    Git Remote Updater by Repository
    Git Remote Updater by Site
    Git Remote Updater by Site


    An example of the type of feedback supplied is below.

    Git Remote Updater Feedback
    Git Remote Updater Feedback

    Functional Differences

    Git Remote Updater doesn’t seek to provide feedback about the update status of a particular plugin or theme. It simply forces an update based on the installed branch of the remote repository. This is functionally equivalent to a reinstallation or branch switch to the same installed branch of the repository.

  • WP Core Development with Local Lightning

    Firstly, this is only meant as a how I do it. I’m using MacOS.


    It is based on using git://core.git.wordpress.org/. This would be similar to WP core development on a regular install using the WordPress Beta Tester plugin set for bleeding edge nightlies.

    • Create a new site in Local Lightning.
    • From Local Lightning Open Site Shell
    • You should cd into /app.
    • Move the wp-config.php and delete /public.
    • Then clone core.git.wordpress.org to /public.
    cd ..
    mv ./public/wp-config.php .
    rm -rf ./public
    git clone git://core.git.wordpress.org/ publicCode language: PHP (php)

    Create symlink of wp-config.php into /public

    ln -sv $PWD/wp-config.php $PWD/public/wp-config.phpCode language: PHP (php)

    The following can be used to keep your clone less cluttered. Add ./app/public/.gitignore with the following data.

    Add the above from my gist to the /app directory with the following command.

    curl -o ./public/.gitignore https://gist.githubusercontent.com/afragen/43dfff563e942353d866c81904498cb2/raw/.gitignoreCode language: PHP (php)

    Add setup-phpunit.sh script for testing.

    curl -o setup-phpunit.sh https://raw.githubusercontent.com/afragen/setup-phpunit/master/setup-phpunit.shCode language: JavaScript (javascript)

    I鈥檝e written a script to aid in applying patches or changesets from core.trac.wordpress.org. It is added to the commands as well.

    curl -o apply-trac-patch.sh https://gist.githubusercontent.com/afragen/977d765414189d5f5fae42215fe92a27/raw/apply-trac-patch.shCode language: JavaScript (javascript)

    Run setup-phpunit.sh script.

    Update trunk via git pull from /app/public using Open Site Shell

    Below are the list of sequential commands after Open Site Shell

    Someway to automate this setup in a one-click install or an advanced setting on the install would be tremendous. see https://localbyflywheel.com/community/t/feature-request-add-simple-install-of-a-wp-core-dev-environment/12985/3


    A separate one-click install using git://develop.git.wordpress.org/ would also be great but that will require installing npm and setting the database to display /build as the home URL endpoint.

    As above you will need to create a new site in Local Lightning and then Open Site Shell from Local Lightning.

    To make this function, before running the commands you must ensure that your local environment has wget and npm installed. If you’re on a Mac I highly recommend using Homebrew and brew install wget. Installing npm using Homebrew can be done but isn’t necessarily the recommended method.

    I have to give lots of credit to Sal Ferrarello for his post WordPress Core Development on Local by Flywheel and to Kees Meijer for his script setup-phpunit.sh

    I had to modify the setup-phpunit.sh script to work with Local Lightning. It is heavily biased towards using MacOS. I’m hoping I can get a little help making it more universal. My version is on GitHub

    Here’s the sequential commands I’ve adapted from Sal’s post to use the git://develop.git.wordpress.org repository.

    Update for Apple Silicon:

    Use nvm to install node version 16.x. Node v16.x will build on Apple Silicon. The following uses the command line. I have nvm installed via the zsh-nvm plugin for oh-my-zsh!

    • nvm install 16, allow a few minutes to build.
    • nvm use 16
    • nvm alias default 16, sets node v16.x as default for each new shell session.

    This will automatically install and use the correct version of npm.

    I have outlined all of the above in a Trac ticket comment.

    The preceding bases your WordPress installation on develop.git.wordpess.org. You will open your site from a URL similar to mylocalsite.local/build/ and the dashboard is mylocalsite.local/build/wp-admin/.

    One-click Install

    For a one-click install you can use the following commands.

    # Setup environment from core.git.wordpress.org
    sh -c "$(curl -fsSL https://gist.github.com/afragen/e1aa3ffccf1a73618ee6e756bd95d297/raw/core-git-wp.sh)";cd .
    # Setup environment from develop.git.wordpress.org
    sh -c "$(curl -fsSL https://gist.github.com/afragen/e1aa3ffccf1a73618ee6e756bd95d297/raw/develop-git-wp.sh)";cd .Code language: PHP (php)
  • Bringing WordPress Core to PHP 5.6 and Beyond

    In the 2018 State of the Word, Matt told us the plan was to move the minimum PHP version for WordPress Core to 5.6 in April 2019 and to PHP 7 in December 2019. I won’t discuss the irony of WordPress 5.2 being the update that kills support for PHP 5.2, but the coincidence is remarkable.

    Every version of PHP from 7.0 and below has been designated end of life (EOL). Currently, WordPress’ minimum PHP requirement is 5.2.7 which was EOL’d over 8 years ago.

    In the 2018 State of the Word Matt said we would be moving to PHP 5.6 as a minimum requirement in April, 2019 and increasing the minimum to PHP 7.0 by the end of 2019.

    This presentation will attempt to describe the safeguards put in place to avoid breaking the internet. Much of this emanated from a single conceptual Trac ticket.

    Coding for WP Core is different.

    Overview of Servehappy

    It鈥檚 no secret that WordPress has stayed on PHP 5.2 long after it鈥檚 been cold, dead, and buried. The reasoning was simple. WordPress is used by a third of the internet and even though fewer and fewer sites are using these EOL鈥檇 versions of PHP no one wants to break the internet for these people, or anyone else for that matter.

    There has been a concerted effort to work with hosting companies to move the needle and it has worked.

    Servehappy is the code name for the Site Health project. The goal of this project is to put safeguards in place to protect as many users as possible during this transition. Nothing is perfect and the numerical combinations of WordPress versions, PHP versions, plugins, and themes is astronomical.

    WordPress PHP versions

    There have been several methods at play.

    In WordPress 5.1 the following was added.

    • The dashboard call out to update PHP.
    • Protection from installing plugins whose requirements are higher than the site can provide.

    In WordPress 5.2 the following are scheduled to be added.

    • White Screen of Death (WSOD) protection.
    • Protection from updating plugins whose requirements are higher than the site can provide.
    • Protection from activation of plugins whose requirements are higher than the site can provide.

    Many wonderful people have been involved in creating these solutions and they are led by Alain Schlessera and Felix Arntz. I made a conscious decision to participate in this project. Yes, code was involved but like most things in life. You have to show up.

    “Decisions are made by those who show up.” – Aaron Sorkin, The West Wing

    In this case it meant making time to show up and participate in #core-php and #core Slack meetings.

    Sometimes it meant creating solutions/patches in Trac. Sometimes it meant testing other patches. Sometimes it meant reporting issues or problems.

    There are many more concerns when coding for WordPress Core than when coding for your own projects. It may be having a higher priority for accessibility or translation readiness, but it shouldn鈥檛.

    What I learned was mostly it鈥檚 about having to support new functions and filters with an obsession towards backwards compatibility. What this means is that a hard-coded solution is more likely to be acceptable than a more versatile modular solution.

    Of course that shouldn鈥檛 stop you from creating a solution that utilizes modern techniques but the solution must work within the minimum PHP requirements of Core.

    Update PHP Callout

    One of the first things created for the Servehappy project was a dashboard callout to update your site to a current PHP version.

    dashboard callout
    Dashboard callout

    Along with this callout is the Update PHP page containing reasons why you should update PHP as well as information on how to get help from your web host on actually updating your PHP version.

    In WordPress 5.1 code was introduced to provide a check against a Plugin鈥檚 reported compatibility with either WordPress Core or PHP. The plugin developer would declare these minimum requirements of both WordPress and PHP in the plugin readme.txt file.

    Plugin Installation

    The code check will disable the Install button in the plugin card in the plugin search window and provide information as to why the the plugin cannot be installed.

    install screen plugin cards
    Plugin Install Screen
    Install screen view details
    Plugin Card View Details iFrame

    Plugin Updates

    On schedule for inclusion in WordPress 5.2 is the automatic disabling of plugin updates for plugins that don鈥檛 meet the WordPress Core or PHP version requirements as listed in the plugin鈥檚 `readme.txt` file.

    Plugin updates can occur in two locations: the `plugins.php` page and the `update-core.php` page. Disabling updates from both of these locations was introduced in separate Trac tickets, the plugins screen and the updates screen.

    Plugins page
    Plugins Page
    Updates page
    Updates Page
    Plugin Card View Details iFrame
    Plugin Card View Details iFrame

    Plugin Activation

    One of the final pieces was to disable activation of a plugin if it didn鈥檛 meet the WordPress or PHP compatibility requirements. With some help from others I was able to use the `get_file_data()` to parse the plugin鈥檚 readme.txt file headers.

    If a plugin doesn鈥檛 meet the minimum compatibility requirements a WP_Error is generated, the plugin is not activated, and the user simply needs to use the browser鈥檚 back button to return to their site.

    Part of the original patch was also adding 2 additional plugin file headers, Requires WP and Requires PHP so that plugins that exist outside of dot org and don鈥檛 have a properly formatted readme.txt file could still designate their plugin requirements. This was removed in the commit, opened in a separate Trac ticket.

    Activation Error
    Activation Error

    WSOD Protection

    One of the primary focuses of Servehappy was White Screen of Death (WSOD) protection. It was thought that after an update it would be a huge benefit to be able to create a sandbox for the site so that if a plugin or theme caused a PHP Fatal to occur error it would be a simpler process to access the backend of the site and either effect a change to the plugin or to disable the plugin entirely.

    This patch was initially committed for WordPress 5.1 but do to late identification of potential security issues the commit was reverted. A different idea on implementing this is being developed for WordPress 5.2. This new method mitigates the security issues raised in the initial patch. Here is the official post on WSOD protection.

    WSOD Error
    WSOD Error
    WSOD email
    WSOD email
    WSOD dashboard notice
    WSOD dashboard notice
    WSOD disabled plugin
    WSOD disabled plugin


    I don鈥檛 really think anything is perfect, but I believe the safeguards created and included in WordPress 5.1 and 5.2 take us most of the way there.

    Building a better mousetrap can鈥檛 prevent a more determined mouse from success, or in this case failure.

  • Translations Updater and Easy Digital Downloads

    My Translations Updater Composer library also works for any plugins or themes that are using EDD Software Licensing. I have recently written about the basic purpose and function of the Translations Updater library.

    EDD Software Licensing Integration

    As of EDD Software Licensing v3.6, there are a couple of action hooks in the plugin/theme updater samples that allow for this integration. As part of the setup for using EDD SL, you need to create a new EDD SL updater class with a configuration array customized to your plugin.

    This array is contains data regarding the specific plugin or theme that uses EDD Software Licensing. Integration with the Translations Updater library only requires the addition of 2 elements to the configuration array and a slightly different command that runs the translations updater.

    The additional array elements are a designation to where the translations repository is hosted, GitHub, Bitbucket, GitLab, or Gitea, and the URI to the repository.

    鈥榞it鈥 => 鈥榖itbucket鈥,
    鈥榣anguages鈥 => 鈥榟ttps://bitbucket.org/afragen/test-language-pack,Code language: PHP (php)

    Specific instructions are in the GitHub repository.

  • Translations Updater

    As part of the GitHub Updater I introduced a process for independent language pack updating. The normal process is to include translation files, as part of your plugins, in a /languages directory inside of your plugin and load them via load_plugin_textdomain(). This also works for themes.

    Decoupled Language Packs

    If your plugin is in the dot org plugin directory you benefit from translations that are done by the community on GlotPress. If your particular WordPress installation is localized and the plugin has a translation file for that locale, the translation file will be automatically added and none of the other unused translation files will be added.

    These translations take precedence over those included in your plugin as of WordPress 4.6. If there are updates for the translation file, they will be added via the normal dashboard update process.

    This allows for a decoupled language pack updating experience where the plugin doesn鈥檛 need to include additional files that can contribute significantly to the overall plugin size; but can benefit from maintaining the translations independently from the main plugin.

    Get Your Own Decoupled Language Packs

    The language pack updating method I created in GitHub Updater works in the same manner as in the dot org plugin directory. The developer maintains a separate repository that contains the language packs and the Translations Updater code independently installs the needed translation files. This allows for a more efficient method of distribution of language packs and allows the main plugin and translations to be developed and maintained separately.

    composer require

    Recently I have converted the Translations Updater to a Composer library. In this way it can be installed in any plugin or theme via composer require afragen/translations-updater:dev-master and decoupled language pack updating can be used. This does require a separate, public repository that contains the translations files.

    I have created a Language Pack Maker library that will create the language packs from a folder of translation MO/PO files and create a language-pack.json file that contains the data regarding the current state of all the language packs.

    Real World Example

    I maintain the translations for GitHub Updater using this method. What I do is maintain the public repository of translations and take PRs for updated or new translations. These PRs are only for the MO/PO files. I would then update the repository locally where I would run the Language Pack Maker and then push the new language packs and language-pack.json to the public repository.

    As always, ask questions. I鈥檓 happy to explain in more detail as needed.

    Update: The latest version of the Language Pack Maker only requires PO files. It will create MO files and JSON (JavaScript translation files) as appropriate.