 Dave Ryan is an interdisciplinary WordPress developer at Bluehost where he focuses on helping build WordPress and supporting the WordPress community. In the past, Dave has worked for large publishers and universities scaling high-traffic WordPress sites by blending his skills for information design, journalism, and web development. David Ryan. Thank you, David. Hey, everyone. It's really great to be here today. I just wanted to start out by saying a big thank you to the WordCamp Orange County organizers. They've put on a really great WordCamp this weekend, so thank you so much for all you guys have done this weekend. So today we're here to talk about something that I never knew would be a passion of mine. I kind of fell into development sideways through graphic design, and so I always thought I'd be working on pixel-perfect interfaces and never really thought that I'd be tackling command lines, but I've really kind of grown to like them, and they're very sort of singular focus. So we're gonna dive in. And I am Dave Ryan, AD with a zero on Twitter and other places if you're looking for me. So what is WPCLI? Who here in the room has used WPCLI? Yeah, okay, so about half. So just real quick, WPCLI is another interface to work with WordPress. So on the left here, we have the WordPress admin, right? It's the hello world post that comes with WordPress, and that is our post list in the admin. And then on the right here is a screenshot of my terminal window where I'm logged into my server, and if I use the wp command, and then I use the post list sub command, I can get a list of posts, and I get that kind of wonky little table in there with the ASCII characters, but it gives me really the same data. So the WPCLI can be used to view or modify any data in WordPress, anything you can do in the WordPress admin, as well as run some server side code. Maybe you're jumping into your MySQL shell, maybe you're executing a file or evaluating some code, but it can go beyond just working with data in WordPress. You can use it to run things outside of WordPress, and you actually don't even need to load WordPress at all to use the WPCLI. So I also like to think of the WPCLI kind of similar to a bank's phone tree. When I call my bank, they ask me to authenticate with the last four of my social or the last, you know, four characters in my account number. Same way when I SSH into my server, I'm giving some kind of identity file and username when I log into my server. So once I'm in there, I'm presented with a menu. Press 1 to speak with a representative, press 2 for a status update on this, press 3 to file a fraud claim, and similar thing, if I go into WPCLI and I go into my terminal and I type in WP, I'm going to get a menu with all of my options that I'd have to work with. So maybe it's posts. Maybe I want to list in my posts or I want to update a post or insert a post or delete a post, can work with users, can work with images, can work with all the data that's stored in WordPress. So why would someone want to use WPCLI over the WordPress admin, right? You know, the WordPress admin is pretty, pretty intuitive. It's pretty easy to log into. What's the benefit of using WPCLI? Partially, it's time. I'm not a big believer in saving time, a big believer in reallocating time for more useful tasks, and if you're doing repetitive tasks, say, updating a custom field on 50 WordPress posts, clicking through 50 WordPress posts, finding your meta box, scrolling, yeah, it's pretty quick, it's pretty easy, but that time adds up over time. And if those are tasks that you're doing repetitive every single day, there's ways to kind of automate that with WPCLI and not necessarily save time, but you're going to spend a lot less time on that task and then be able to use that time for something else. So the other reason you'd probably want to consider using WPCLI over just using the admin interface is that you can do some really cool things with it. In addition to getting that table, I can get a JSON formatted array here of that post data. I can also get a CSV or a YAML representation of that post data. And there's often multiple ways to do something with WPCLI. If I have an option in my WordPress database called WCOC is awesome, and I want to update that, I can do it through the option update command. I can say WPEval to run some PHP code and then just straight run some code in my shell right there. I can say WPDBQuery and drop into a MySQL shell, or I could write a custom command that handles this on its own. There's also some additional third-party packages that you could use. There is a WPCLI restful package that lets you interact with the WordPress rest API via the WPCLI, and that package would let me also update this option. But in addition to just running simple commands, working with one type of data, the real power of WPCLI I think comes in and the workflow is in chaining that you can do. So I used to work in enterprise publishing, and sometimes our editorial team would put together, say, 50 posts to go and publish, and we would schedule them in WordPress, which would create a cron job in WordPress to go and publish these posts, and sometimes those cron jobs would fail. And so we would get a support ticket from our content team saying, huh, why did my content not publish? Can someone help me with that so that I don't have to click through 50 posts? So in this query here, I'm saying get me a list of posts, and I want to get the ID, but in the category 3578, so whatever that category is, let's say it's my environment category, I'm going to get all my posts back in my environment category, and then using a little tool called xargs, I can pass the results of this list. We're just going to return me a list of IDs to the next command. So here, I say I have one parameter. This becomes my variable in these little brackets. You can actually use other characters there, but I like those. Execute a WPPost term set. So for every post in this category, go and make it a video or go and publish that post. So you can take the results of one query and pass them to your next command. And there's a few ways to do this. You can also use some really cool operators, like if you're working in a multi-site installation, and you have, say, 10 sites and you need to update a term meta key. You rekey the key in your PHP files and you need to switch from your old key to your new key. You can do a search replace, but you can use that wildcard star operator to say, just loop over any table that is a WP term meta table. So why WP CLI is great? We can chain these commands together, which gives us powerful conditions and queries and logic. There's also a prompt flag by type WP dash dash prompt. That's going to enter an interactive mode that's going to let me step through each of the options. So I think one of the scary things about the CLI is it feels like there's all this stuff that you need to learn and memorize to be able to use it. And there are two commands that really make it much easier that you don't have to memorize most of the CLI. One of those is prompt, which puts me in that interactive mode. It tells me what each potential option or flag is, and the other one is help. I can say WP help posts or post, and that will give me all of the subcommands available on post. And within the WP CLI, there are 44 core commands, and so there's a ton of possibilities for how you can use those core commands and in addition to those core commands, there are dozens of popular packages. A package is sort of like a plugin for WP CLI that you can install and it works just as a custom command. So why would someone want to go and create custom commands? I have all these 44 commands in core. I have dozens of others. Why would I need to go and make my custom command? Well, I think part of one thing if you've started to use WP CLI enough that you've started to do is you've probably started to execute WP CLI commands in a bash file, or maybe you've stored them in an alias on your machine. And that's great. It's a quick shortcut to get to that command and run it in a way that makes sense to you. But there are some downsides because hard drives fail. Shell scripts can have permission issues and often somehow we don't commit those to version control as much as we really should. And it's easier to run defensive checks in your code in PHP than it is in bash. So you're more likely to do them. And so I think it's much easier to create a safe custom WP CLI command than a safe bash script. So the other cool thing is that by running a custom command, you're not necessarily leaving all the benefits of a bash script behind you. You can intermix shell commands and PHP. So it's kind of like being able to write PHP in a bash file. The other benefit I didn't mention is that, so those custom commands can live in both the WordPress plugin or one of those WP CLI packages. You can also install a plugin or package remotely on another server using your local copy of WP CLI. So you can add an alias to VBV or Lando or local with Flywheel that lets you communicate with your remote server and then just use an alias to say WP production, you know, plugin install query monitor. So you can use those aliases to have one WP CLI talk to another. And that's really where if you're managing an agency, if you're managing multiple installations, configuring that YAML file to have your environments in it and being able to use WP CLI from one location instead of having to learn how to SSH into 17 different servers. That's where the magic can really happen. You could also set up a mix of aliases. So I could say WP all install classic editor, which was something that the WP CLI account tweeted out before the 5.0 launch. So if I manage 100 sites through WP CLI, that would let me install that plugin 100 times in different sites simultaneously. So how do I register one of these custom commands? They're pretty straightforward if you've worked with PHP. They're much simpler than I thought they were. So before you use any of the WP CLI internal API, this add command, you really want to make sure that it's available. So make sure you have some kind of defensive check before you try to add these commands. So if WP CLI isn't available, stop running. But here I'm going to add my command. I have my WP pu pu pu command here. And here in the second parameter, it's just a callback, a callable function. So that could be here an anonymous function. It could be a function name. It could be a class. It could be a namespace class. And then that third parameter there, which is an array, just has a bunch of options you can put the descriptions in for your help command. You can put in like a long description, a short description. But then there's also this concept of when. The same way that WordPress has a hook system inside of it that says, I want to run this code at this time in my application, there's a hook system in WP CLI. And so if you want to run your command before WordPress loads, which sometimes you have to do if you want to use some of the core commands, there is a core command called WP core download, which lets you download WordPress core. But if you wanted to use WP core download in your custom command, you would need this when before WP load to be able to use that function. So what are some of the properties of a good custom command? One of them, and this isn't really a technical term, it's more of an English term, is parallel construction. And this extends out into naming your functions and other pieces of code. But it's the idea that words have pairings and that you would stop what you started and you would end what you began and you wouldn't start and then end. You want to find these words that make sense together when you're writing commands. So if you have add, don't have delete. You should have create and delete and add and remove. You also want to try to mimic a lot of the arguments in core and as well as just other common CLI tools. I think this is one of the biggest mistakes I've made as I've started diving into custom commands is, oh, let me make a very unique discrete name for this. But that adds cognitive load. It means that that's one more thing someone has to learn about your custom command. So if there's an equivalent or something kind of similar in core or just something that's common in other CLI packages that you use, try to make sure that it mirrors that. So don't come up with your own, like, test flag. Try to use drive run, which is the flag that WP CLI uses to let you execute a command and see what the results would be without actually affecting that result. So in addition to trying to mimic those core and common args, there are a bunch of parameters in WP CLI that aren't global but very common. One of those is the format command. You use it whenever you're working with an object in WordPress. So it's not baked in out of the box that you could say format JSON, format YAML for your custom command, but it adds an immense level of value if you can work with your data in different formats. So, you know, obviously don't over engineer something, but consider adding, you know, a JSON export for your command. Consider adding a CSV export for your command. There's also a few other built in tools, so there's prompts, which let me stop the command from running and ask, are you really sure you want to do this before continuing? Often if you're going to run, you know, a delete or a very expensive operation on your database, you might want to just double check it wasn't an accidental, you know, quick finger typo. At the same point in time, you want to make sure that all your prompts can be overridden programmatically, which means that if you're going to try to stop a command, make sure that there's a way that someone can add a flag that says bypass. Let me just script this so that if you have a bash script or something else that's relying on your command, it can still continue to execute and it doesn't have to have a human there to do it. There's also a progress bar. So if you're running an operation on say a thousand posts or whatever operation your command does is very expensive and it's looping over items, there is a built in progress bar that will create a time estimate for you and all sorts of fun stuff. And you just sort of run a command to say, all right, next tick to the next progress tick mark in your for each loop. And then a dry run parameter is really, really helpful. If your operation is doing something kind of dangerous or that you really would want to add that prompt again, it's really nice to see that, oh, this search and replace is going to affect 23 database rows before I go and actually run those. So now I just wanted to take a quick look at some commands. So I use Lando for my local development lately and I'd say probably about once a week I end up spinning up a new WordPress website and it's fairly straightforward. But it probably takes me about 20 minutes to go through and delete all the default Hello World content and all the widgets and set up all the settings how I want them. So I wrote a little command that I can use any time I want to spin up a new environment and bring down all of the plugins and packages and settings that I want to work in my local dev environment. So here I'm again running a conditional check to make sure CLI is available before I go and try to add a command to it. I'm just using the auto loader here to load in some namespaced files and then here I add my command. So WPCLI add command to the string here, Lando. The second parameter is that callable parameter. So I've given it the namespace for my Lando Cloud City command and then I have an array here where I'm saying run this command for WordPress loads because I'm going to need to do one of those WPCOR downloads and WordPress isn't going to be available yet. So that loads up this CLI file. So I have a namespace so I have to have a use statement at the top so that I can access that global namespace and I'm extending the WPCLI command base class and I have my class command and then right here I have my function install. So in here I do a few basic checks. I check if there is this first args array or what are called positional arguments. So those are whatever happen after a space. So if I say WPPostDelete7, 7 would be my zero argument, my args zero value for that command. And then associative args are flags that have a key and a value. So you have your positional commands which happen in an order after your command or sub-command and then you have flags which are probably a little bit more familiar, kind of like a query string. So here I'm checking to make sure if I've got my first positional argument or if a flag of URL has been set, then create a URL variable. Now if you haven't used Lando, it creates sites by default with a subdomain.lndo.site on your machine. So here I set it up such that if I didn't want to have to type the whole URL every time I use this command, it will check to see if Lando.site is in that string and if it's not, it will just put the value I passed into the URL there. So instead of having to type WP Lando install bluehost and then WP Lando install httpsbluehost.lndo.site, I could just say WP Lando install bluehost and that will install a site at that URL. And here I'm using the internal API, the error to halt and say site URL is required and then here this is one of my biggest tips as you halt or sort of break the output of your command. Try to add some tips in there. For yourself. So try using URL slug or the actual URL and then it's like, oh, yeah, I forgot. I need to go do that and then you can rerun the command. So here I have a public function. Any public function in this class becomes a sub-command. So if I have a public function uninstall, that would instantly become WP Lando uninstall. But protected functions in here do not become commands. So I'd like to have a bunch of logic in the public command and then run most of my code in some kind of protected command or separate those out into multiple functions in the class. So here we can see that I've run an install and I'm saying first run a WP CLI update then download WordPress core then create a WP config file with the following database username options, etc. This is the default that comes with Lando right here so I'm just piping that through and then run a WP core install. And here I'm taking the values that I'm parsed up here and install the URL and the title of the site and I'm running an install saying set up this WordPress site, expect it to be at this URL. Expect this title, set up an admin user called Lando with a password called Lando. Install a bunch of those WP CLI packages that don't come with core, that have some cool features. There is a WP revisions CLI package in here that lets you access post revisions. That's pretty cool. Another one is WP sec which will compare your install against the WP Vuln database. Yes, I can. Run command hook is really cool. It has some features kind of similar to New Relic if you've ever used New Relic where you could look at a hook or query monitor has some similar features where you can say what is attached to WP NQ scripts so I could say WP hook WP NQ scripts and see all of the code that's executing on that action hook in WordPress and so if I'm trying to debug something and I'm trying to see is my action running that would be a cool way to see my assets are a registrator that code isn't quite making it there. Site empty is a really great command. It will delete all the default content in WordPress without messing with the settings table. So it will delete posts, pages, widgets, any default data in there. And then that WP CLI restful package is where you can interact with the rest API via the WP CLI and it's awesome. So I run some updates. I set some options. I set up some user meta. I prefer my library in the list mode instead of the grid mode. I wipe out some of the default themes. I do a plugin install and here I have an array at the top of this file called developer plugins and I just run a little implode and separate each item in that array with a space so I can say plugin install and it will space out each of these plugins, download seven, nine plugins there and that's about it. At the bottom of this file another thing I recommend if you're making one command you're probably going to want just some helper functions within your class. If you're making a series of commands I'd recommend making an abstract class of your own. Maybe abstract Dave's abstract command extends WP CLI command and then extend that class from all of your command classes so you can put all of your helper functions in one place. We're going to risk a live demo. How about that? It's fun to talk about this stuff but being cooler, I actually see it. I'm going to get my Lando machine started here. I'm pretty sure it's already got it. When I type my commands you're going to see that I prefix them with Lando so I'm going to type Lando WP instead of just WP CLI. That's just how my machine is set up to access CLI. You could either SSH into your Lando box or it has a set up of command pass-throughs where when I type Lando WP it takes that command and then it goes in and executes that in my remote box. It's just a little shortcut that lets me not have to SSH in. I'm going to first I didn't clear this out. We're just going to set it up from the beginning. How about that? I'm going to make a directory for Camp OC say Lando and knit make a WordPress site. Part of the reason I wanted to do this is that you can see WP CLI being downloaded by Lando. WP CLI is in what's called a FAR package. A FAR package in the simplest way. It's labeled into one file in some way that's almost like a zip directory of code but it lets you put it in a spot on your system where you can make it executable and then whenever you type WP it's just piping all of your commands through to this one I guess. So if I type Lando WP oh great the joys of live demos let's see if I have another site set up. I'm going to get that started up should already be up. So when I type WP this is what I would see on my screen. So manage WordPress through the command line and then I have a list of all of the sub commands that come in core as well as anything that I have installed via a package. So most of these are default commands. You might notice that one of them is EIG that's going in my blue host site we have a command called EIG and then I've installed some other packages on this site so you can see that the REST command is available. So I can say Lando WP help REST I can see all the commands that come available with that package so I could say WP REST page and start interacting with page and points on the WordPress REST API doctor sorry the WP doctor command is really kind of cool. It lets you run a series of checks to diagnose issues with your WordPress site. It's not one of the core packages but it was one of those packages I had in my package install list. Here I don't know if this is going to work because I don't know if I already have it here but I'm going to do a Lando WP package install and so I'm going to say go out to GitHub so go out to GitHub find this repo and I might get mad because I've already installed it. We'll see. But it's basically going to go out and do a composer install and this is going to take a few minutes but after this is done I can use my WP Lando command within this install to try to run a WordPress install. It's not going to let me do that because I already have WordPress installed but cool. Now I have Lando WP Lando and it's going to say to use this you have to use WP Lando install. Okay. Lando WP Lando install Bluehost and it's probably going to be mad. It says cool. All right. I tried to update WP CLI which is the first thing in my install file but then it erred out and it halted and it ended my instance and it says WordPress files seem to already be there. So I tried to download WordPress a conditional check within the command said WordPress is already there. I'm done. So that is one thing to know about custom commands. If something fails in your command your command will halt right where your failure happens. So if you have other things that you expect to happen after that those will not happen. So it's part of the reason it's really important is it really not empty because the only other thing I have is there's just some CLI commands I've worked on for EIG and one of the kind of interesting use cases I had to solve here is that had about half a dozen commands but the way this was initially set up they were registered under like a dozen different aliases. So I had to have basically the same command under 12 different aliases. So you can use the same command under different aliases if that's something that makes sense in a large company or client you're working with. So all of the commands here are registered to Bluehost as well as to Ipage as well as to EIG. You can loop over all your commands I've run a check here to say is this command supported on on this particular brand maybe have a command that's only compatible with Bluehost but you know Ipage can't use it something like that. So it's a lot of really fun things you can do with WPCLI to look for opportunities to script part of your work flows to look at things you're doing frequently whether that's setting up WordPress installs whether you have a sort of methodical setup that you go through every time you check a site or maybe there's an audit a site audit you do for customers and there's some things you could automate in that but it's also just a really great tool to be able to tie WordPress deeper into your server you can set up crontabs that go out and execute WPCLI commands and really sort of get more automation going in your site that's all I have for today so thank you all for coming but I'm happy to chat after any questions? yeah we're gonna do it live oh that was Ellen I don't know why I can't remember why but I needed Ellen so yeah yeah that's that's public now so if you wanted to go out to github.com slash Dave Ryan take a look at this little custom command class I have set up you can go and modify it to your liking and have a little setup online yeah I don't know just say yes okay sure anyone else yeah they can be so how about I put a link to them in the github repos read me so everything will be on that little github repo Dave Ryan slash WP-Lando there we go so Dave Ryan the D is a zero WP-Lando and if you wanna go and create a custom command and you're not really sure how to get started you can use the WP-Lando to get started there is a command in the WP-Lando called scaffold it will create a boilerplate plugin for you a boilerplate theme unit tests that's all sorts of cool stuff but it will also scaffold a WP-Lando package for you so that you can pretty much just get started and it even generates a functional tests in there and a few other a few other niceties so I'm not sure about what I find myself using most frequently I think it's that's kind of mixed favorite is definitely search replace I think search replace is the most powerful command in WP-CLI it was actually I think not not quite a fork but sort of like a fork and copy of another plugin that used to do it in a graphic user interface in the admin I really like the wild card features and multi-site and the drive run in there is really powerful so if I'm expecting to update 50 rows and I can get it you know oh we were about to update 50 rows if you do this just very very comforting let's go have some lunch thanks so much for coming