Switch Laravel Valet from .dev to .test in three easy steps

Chrome and Safari began forcing https for the .dev domain because someone apparently thought it was a good idea to register as a public TLD. Laravel Valet only produces self-signed SSL certificates though, so I want to keep my local installations served as http. Guess it's time to switch TLDs!

Oh, and don't try to use .local on a Mac because it conflicts with Bonjour local networking. I discovered this with 30 minutes of wasted effort. .test is the way to go.

First, run:

valet domain test

Switch Valet to using the .test domain, which will also update dnsmasq accordingly. Don't try to edit dnsmasq configuration on your own — there are too many ways to go wrong.

Second, run:

wp package install wp-cli/find-command

Install wp-cli/find-command to find all WordPress installs in your Laravel project directory. It's convenient for running one WP-CLI command against all WordPress installs.

Third, run:

wp find ~/projects --field=wp_path | xargs -I % wp --path=% search-replace '.dev' '.test' --all-tables

Run wp search-replace against all WordPress installs to replace instances of '.dev' with '.test'. ~/projects is my Valet project directory, and --all-tables ensures the procedure is run against all database tables.

Et voila! You've switched Laravel Valet from .dev to .test in three easy steps.

Good first Gutenberg issues

Want to submit your first pull request to Gutenberg? Here are a few good first issues to get your feet wet:

Read through the contributing guide for details on how to get started. Feel free to ask questions on the specific issue, or join us in the #core-editor channel with any questions you might have.

Analysis of distributed host testing failures

Since August, several WordPress hosts have been running the WordPress PHPUnit test suite on their infrastructure. Read this post for more background.

In total, six failures have been observed:

  1. r42421 – REST API: Return the proper status code for failed permission callbacks in WP_REST_Server->dispatch()
  2. r42037 – Customize: Support instantiation of partials with flat/unwrapped params for parity with controls, sections, and panels in [41726]
  3. r41764 – Widgets: Fix jshint error in media widget
  4. r41717 – Multisite: Replace calls to refresh_blog_details() with clean_blog_cache()
    • 1 of 1 hosts reported failures.
    • Travis CI reported a success. Root cause of host failure is unknown.
  5. r41614 – Users: There is not, in fact, 12345 users on every WordPress installation
  6. r41574 – I18N: Merge two similar “Cannot set parent term” error strings

Interesting! But also not very interesting because there's not much to see.

Landing Gutenberg in WordPress 5.0

Some incomplete ideas I've been noodling on that I want to make public.

Ultimately, the goal is: the vast majority of WordPress users are excited and should be able to use Gutenberg on day one. Fundamentally, this breaks down into two objectives:

  1. Make the end-user experience is so good that WordPress users actively want to switch to it. We need to continue user testing as we have been, and iterate based on real user feedback. We also need to market Gutenberg — communicate what users should expect and get them appropriately excited.
  2. Mitigate WordPress plugin and theme incompatibilities, to minimize conflicts that would cause WordPress to fall back to the classic editor. Success is defined by the majority of WordPress users being able to use Gutenberg on day one. If too many can't use Gutenberg because of conflicts, then we've failed at launch.

I've been brainstorming some strategies for the latter, which really is two parts: identification and mitigation.

First, we need to identify the true extent of the problem: what plugins and themes are incompatible with Gutenberg, and in what ways are each incompatible? Some automated ways we can produce this data includes:

  • Manual/automated analysis of action and filters usage, etc.
  • Activate each in an isolated environment and take before/after screenshots of the editor screen.

But, I'm thinking good ol' fashioned crowd-sourcing might be most effective. What if WordPress users had an easy way to report whether a given plugin or theme was compatible with Gutenberg? We could collect this data in aggregate to get a good sense of what types of incompatibilities we should expect, and where we should focus our efforts.

Once we've identified the plugin and theme conflicts, we'll need to mitigate them. Doing so will require excellent documentation, so authors more easily understand the changes they'll need to make, and deputizing other developers to help with the outreach process.

Seeking hard problems

It's that time of year again (where my schedule empties out), so I find myself in search of a really hard problem to work on. Some problems that have piqued my interest:

  • Affordable housing. Did you know that affordable housing is defined as paying 30% of income or less on housing? And did you know that Washington County, where Tualatin is located, has a gap of ~14,500 houses and growing? I didn't either until about five months ago. Even if you can still afford your housing, this is a problem the entire socio-economic spectrum should be working on.
  • Government technology. The USDS is really cool and having an amazing impact. You should listen to Jennifer Pahlka's SALT talk, "Fixing Government: Bottom Up and Outside In". I wish there was a similar initiative in Oregon. Is there one?
  • Landing Gutenberg in WordPress 5.0. Gutenberg is a revolutionary editing interface. So revolutionary, in fact, that it's one of the worst-rated plugins in the WordPress.org directory. Getting from where we are now to happily shipped in core is going to be a challenging, multi-faceted initiative.

Let me know if you have any input on these problems, or whether there are others I should be considering!

WordPress needs automated browser/integration/end-to-end testing

One thing I love most about WP-CLI is its Behat-based test suite. In fact, if you consider WP-CLI successful at all, I'd attribute said success to the test suite.

Having a great test suite ensures exceptional build quality because:

  • It’s easy to write new tests, which means they actually get written.
  • The tests interface with a command in the same manner as users interface with a command, and they describe how the command is expected to work in human-readable terms.

WordPress should have amazing integration tests too.

Historically, PhantomJS has been the de-facto standard for headless browser testing. PhantomJS is also Yet Another Abandoned Open Source project, and generally a pain to deal with. But we're in luck! Headless Chrome shipped in Chrome 59. It's pretty amazing. For your next side project, try out the Puppeteer library.

Now that we're over the headless browser hurdle, implementing automated browser testing is simply a matter of:

  1. Picking a framework. Behat is one option (see WordHat for an example), and Codeception is another. I like Behat, but Codeception is based on PHPUnit which might be a perk.
  2. Setting up a Docker container to provide a WordPress install in an isolated environment. For ease of use, Chrome headless could be provided in another container.
  3. Writing some tests. Prior to writing tests though, it would be helpful to plan out all major UX flows we want to cover with tests.

In fact, I'd argue that integration tests will be key for managing breakage when Gutenberg lands in WordPress core.

What would be really neat is if the test suite supported importing some base "state", such that it'd be possible to easily run the same test against dozens of scenarios. This would let us perform an experiment where run the test suite against every plugin in the WordPress.org plugin directory.

Next-generation managed WordPress hosting

What if there was a managed WordPress company that got rid of its servers altogether, and focused on helping you achieve the best possible results from any infrastructure on the market?

Here’s how it might work:

  1. When you sign up for an account, you connect your Google Analytics account, so the service has intelligence about your current traffic patterns.
  2. Based on expected resource utilization, the service suggests a server configuration on AWS, Digital Ocean, or Google Cloud Platform. Once you select your preference, the service provisions a WordPress-optimized hosting stack on the provider.
  3. As a part of the offering, the service provides a best-in-class object-cache drop-in, CDN-based full-page cache, Elasticsearch integration, etc.
  4. Over time, as the service passively monitors the performance of your WordPress instance, it offers suggestions for additional application-level optimizations (think New Relic but with a higher-level understanding of WordPress).
  5. Over time, the service also passively monitors your resource spend. If it detects there’s a more price-performant option on the market, the service makes it possible to migrate your site to new infrastructure seamlessly. As an example, a client of mine was spending $1k+ per month on Amazon’s CloudFront CDN. Now they’re spending $20/month on CloudFlare.

The target market is every customer subject to “Contact us for a quote” pricing (typically $2k/month and up). The service would enable a non-technical company to run their website in the price-performant public cloud without needing to internalize the DevOps cost.

Some existing options include ServerPilot and Laravel Forge, but neither focus deeply on WordPress.

Crazy?

One simple trick to clean up your WordPress database

Most WordPress sites likely run Akismet, which comes bundled with every new copy. What they probably don’t know is that, over time, Akismet will cause your wp_commentmeta table to balloon in size.

Use wp db size --tables to see the size of all tables:

$ wp db size --tables
+-----------------------+--------+
| Name                  | Size   |
+-----------------------+--------+
| wp_users              | 9 KB   |
| wp_usermeta           | 66 KB  |
| wp_posts              | 274 MB |
| wp_comments           | 48 MB  |
| wp_links              | 3 KB   |
| wp_options            | 41 MB  |
| wp_postmeta           | 25 MB  |
| wp_terms              | 796 KB |
| wp_term_taxonomy      | 621 KB |
| wp_term_relationships | 905 KB |
| wp_termmeta           | 48 KB  |
| wp_commentmeta        | 687 MB |
+-----------------------+--------+

Whoa! wp_commentmeta is way larger than I’d expect it to be. What’s going on there?

Let’s take a look at what keys are used:

$ wp db query "SELECT DISTINCT meta_key FROM wp_commentmeta"
+-----------------------+
| meta_key              |
+-----------------------+
| akismet_result        |
| akismet_history       |
| ERRating              |
| akismet_user_result   |
| akismet_user          |
| akismet_rechecking    |
| akismet_as_submitted  |
| akismet_pro_tip       |
| _wp_trash_meta_status |
| is_customer_note      |
| rating                |
+-----------------------+

Hm. Can’t quite tell what might be a large one from that set. Let’s look at a random comment:

$ wp comment meta list 659968
+------------+----------------------+-----------------------------------------------------------------------------------------+
| comment_id | meta_key             | meta_value                                                                              |
+------------+----------------------+-----------------------------------------------------------------------------------------+
| 659968     | akismet_result       | true                                                                                    |
| 659968     | akismet_history      | {"time":1503414726.1497,"event":"check-spam"}                                           |
| 659968     | akismet_as_submitted | {"comment_author":"coach handbag tassels embroidery","comment_author_email":"[email protected] |
|            |                      | il.com","comment_author_url":"http:\/\/www.bestcoachbag.store\/accordion-zip-wallet-in- |
|            |                      | signature-embossed-leather-p-37.html","comment_content":"Hi, i think that i saw you vis |
|            |                      | ited my site thus i came to return the favor?I'm attempting to find things to enhance m |
|            |                      | y website!I suppose its ok to use some of your ideas!!","comment_type":"","user_ip":"58 |
|            |                      | .19.83.5","user_agent":"Mozilla\/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Mozill |
|            |                      | a\/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; .NET CLR 1.0.3705)","blog":"http:\ |
|            |                      | /\/example.com","blog_lang":"en_US","blog_charset":"UTF-8","permalink":"http:\/\/exa |
|            |                      | ple.com\/one-two-three"}                                           |
+------------+----------------------+-----------------------------------------------------------------------------------------+

What on Lord’s earth? Why is Akismet storing a copy of every comment in comment meta? I wonder how large that is in total:

$ wp db query "SELECT sum(char_length(meta_value)) FROM wp_commentmeta WHERE meta_key='akismet_as_submitted'"
+------------------------------+
| sum(char_length(meta_value)) |
+------------------------------+
|                    567045828 |
+------------------------------+

Ah, I see. Akismet’s akismet_as_submitted entry is 567 MB of the 1 GB total database size. That makes sense.

PS I tried to list the total comments but then I ran into a WP-CLI bug. Que será, será.