Half-Elf on Tech

Thoughts From a Professional Lesbian

Author: Ipstenu (Mika Epstein)

  • Defines, Variables, and Plugin Dirs

    Defines, Variables, and Plugin Dirs

    If you’ve spent any time looking at PHP code, then you’ve seen defines and variables

    Defines

    A define looks like this:

    define('SOMETHING', true);
    

    This makes a global constant that can be used anywhere. In the case of WordPress, it means they can be used across multiple plugins or themes. This is very useful if you make a suite of plugins that all have the possibility of using the same API key. Then you can tell a user to put define('MY_PLUGIN_API', '123456'); in their wp-config.php file, and tell your code to check for the define.

    A define also cannot be redefined. If you call it twice, you get errors, so you should be as unique as possible when creating yours.

    Variables

    A variable, meanwhile, looks like this:

    $SOMETHING = true;
    

    Variables only exist where they are, so if I have one in one function, I may not be able to call it in another. You can make global variables if needed, and in fact if you don’t, the variable won’t be available to all functions.

    Which Should I Use?

    Keeping in mind that defines are constants and variables are, well, variables, the idea is that a constant should be used for things that should not change in the running of the code. It also makes your code easier to maintain if the things that must be constant are explicitly so.

    So what’s a good define? Actually not really this:

    define('MY_PLUGIN_VERSION', '1.0');
    

    This is a constant, it’s something you should be setting and it shouldn’t be overwritten, but actually I’d want to make it a database field to check on before upgrading. Remember, you should be changing that define on upgrade, so having it be a declared constant is a little odd. Now that said, it is a good one when you consider you don’t want someone to willy-nilly override it. Except … what if you do? What if you want someone to be able to change it back to re-run an upgrade?

    So then really not this either:

    define( 'MY_PLUGIN_PATH', plugin_dir_path( __FILE__ ) );
    define( 'MY_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
    include( MY_PLUGIN_PATH . 'lib/asset.php') ;
    

    Someone’s probably thinking that their plugin directory is a constant and, thus, should be defined like that. Maybe, but it’s a pointless define in WordPress. You already have plugins_url() (which this example isn’t using) but it’s really where people use that code that makes no sense. That’s why I included the second line there. That’s what they use it for, and it means two lines of code for one function call.

    This actually magically becomes a good define when you have a complex plugin with multiple files and you need to grab the main plugin directory in sub-files. But if your plugin is one file (and yes, that’s where I see it the most), it’s overkill.

    This is a good define:

    define('MY_PLUGIN_APIKEY', '123456');
    

    But that should never be in your code itself. Not for WordPress at least. That should be in the wp-config.php, like I mentioned before.

    Basically … there aren’t great reasons to use defines unless you want things that never change. define('MY_PLUGIN_SUPPORT_HOME','http://help.example.com'); would make sense to me, as would other contact info you want to make sure is never subverted.

    What do you think define is best for?

    I’m interested to hear what you guys like to use defines for. I’m certainly guilty of using them for some pretty silly reason.

  • March into CloudFlare

    March into CloudFlare

    “Eating your own dogfood” is a colloquialism that describes a company using its own products or services for its internal operations. Microsoft supposedly invented it in the 1980s.

    DreamHost is partners with CloudFlare. I’ve tried time and again but I get hung up when we start talking about the delay in caching and the proxying of caches. Still, I know me. I can do things better and provide better support if I use a thing. I’m so good with PageSpeed and wp-cli now because I use them regularly.

    So it was time to knuckle down and use it for more than just a week. I was committing to a month on CloudFlare!

    Pick One Domain

    I decided to only do this for one domain. My busiest, and not a multisite, because I wanted to create a ‘real user experience.’ I didn’t use my company account. I made a new CloudFlare account, added this domain, and started there. I didn’t want Multisite because that’s where my store is, and if I screw up the busy site, I don’t lose money or risk people’s information. While I’m sure it can be used safely, I knew I was going to experiment a little, so I wanted to protect myself and them

    Turn on CloudFlare, Turn off PageSpeed

    You heard me. I turned PageSpeed off for the domain I’m testing on. I love PageSpeed, but after talking to some people, I’ve been wondering about how well it handles things. Also with SPDY and HTTP/2, compressing HTML is less and less of a concern. I wasn’t sure if there was a benefit to having everything be filtered and compressed before it loaded. Was I making the experience worse? After making sure I still had mod_cloudflare active and up to date, I used .htaccess to turn off PageSpeed.

    Break Your Code Flow

    I need to point out that CloudFlare warns you about this one. They tell you that if you need to SSH, you should use a different record or the IP. That wasn’t a big deal. I’d kept ftp.example.com separate for FTP anyway. All I had to do was change my SSH aliases to point there as well.

    I forgot that I use Git on my own server. I wanted to update a script and Coda hung. It took my brain a moment to remember that I was using Git with SSH so I had to remember how to change the remotes on Git:

    $ git remote -v
    # origin  me@example.com:USERNAME/REPOSITORY.git (fetch)
    # origin  me@example.com:USERNAME/REPOSITORY.git (push)
    

    This was a problem. My example.com main domain hit CloudFlare. So I had to change that to use ftp.example.com as well. That wasn’t too hard, just running this 6 times.

     $ git remote set-url origin me@ftp.example.com:USERNAME/REPOSITORY.git
     $ git remote -v
     # origin  me@ftp.example.com:USERNAME/REPOSITORY.git (fetch)
     # origin  me@ftp.example.com:USERNAME/REPOSITORY.git (push)
    

    Still, I felt pretty silly!

    Break Your Email

    Same song, second verse. I had to add in a CNAME for smtp.exmaple.com on CloudFlare because I use that to send emails and I use mail.example.com to receive. By default they know mail should be ignored. The scan didn’t pick up smtp. I forgot about it until I tried to reply to an email.

    Break Your Tools

    Not the ones on my computer. My other CMSs broke. CloudFlare is used to WordPress, and has Five Easy First Steps to using WordPress and CloudFlare.

    In that document, they specify this:

    Create a Page Rule to exclude the wp-admin or wp-login sections from CloudFlare’s caching and performance features. You can access PageRules in your CloudFlare ‘Settings’ options.

    e.g.

    *example.com/wp-admin/*
    *example.com/wp-login/*
    

    Why do this?

    While there is not always an issue, we have seen instances where optional performance features like Rocket Loader may inadvertently break certain functions (editors, etc.) in your WordPress back end.

    Except there are major problems (besides the fact that the second example should be *example.com/wp-login* without the trailing slash)! The free version only gives you three rules. I’m using four apps (WordPress, ZenPhoto20, Yourls, and MediaWiki). That means if I need to white list all of them, I’m out of luck.

    Then there’s the problem that *.example.com matches blog.example.com and www.example.com but does not match example.com and guess what? I’m using example.com without the WWW. I hate WWW. And yes, you can use a naked domain with CloudFlare.

    Break The Site (For One Person)

    Someone pinged me to let me know the site was down in Scotland:

    Website is Offline Message from CloudFlare

    It wasn’t in Manchester or Dublin. It wasn’t in the US. It wasn’t in Canada. I opened a support ticket after making sure that it wasn’t really me. The server was up (all other sites, including this one were up) and I could get the site via anonymous proxies. Only that user had an issue, and I was 100% positive I had whitelisted everything in CSF (it’s the same thing I do for Jetpack). But the website claims a 522 is my server.

    This was never resolved, and demonstrates a major issue in the process. The user was a non-web savvy user. She shouldn’t have to be, though. She just wanted to visit a site and read things. It was very annoying.

    Drop Server Load

    Okay. So this part worked.

    Graph showing Server load on day 3 leveled out

    It only shows up starting day three because I didn’t flip DNS over right away. I had to turn off PageSpeed, upgrade PHP, make sure nginxcp was going to work with it… There was prep work. Day three, the little spikes vanished. You still get big ones because that’s when the server runs backups and upgrades. The little spikes are, normally, when I have a new post on the site.

    There’s been no perceptible change to bandwidth. All other sites on the server are, however, notably faster.

    End Result?

    It looks like CloudFlare worked. The stats say I’m using 25% less CPU with CloudFlare, which is interesting, but now that it’s baked, I want to try something else, just for grins and giggles.

  • Mailbag: SNI Incompatibility?

    Mailbag: SNI Incompatibility?

    Kim asks:

    You wrote an article which does a great job of explaining a number of things. My only question (comments appear to be closed so I could not post there) is the SNI – do you find that there are many people using browsers that are old enough that the SNI creates a problem? I have looked over the list of incompatibles and it does not seem to be that much of a risk, but I thought you might have more concrete information since you’ve been using the setup.

    This relates to how I set up my SSL certificates, which is to use Server Name Indications and have multiple certs on one server with one IP. And the question is “Do we care about the old browsers?”

    Let me quote my coworker.

    IE8 is EOL, XP is EOL. We can’t support things forever.

    XP makes up most of the sites that have issue with SNI so I’ve only found 0.006% of my visitors impacted.

    Yes, I did that math properly. I checked it a couple times.

    No. I’m not worried about SNI and I don’t care. We can’t support old things forever.

  • CloudFlare’s SSL

    CloudFlare’s SSL

    CloudFlare’s been pushing SSL for a while as a new feature. We all know that SSL is a great idea, that any time you have someone logging in, it should be secure. If you’re handling money, it should be secure. If you’re taking any personal information, for god’s sack (sic), make it secure!

    A major problem with this has often been the cost overhead. You can self-sign your certificates, but that pops up with other errors for people. Really what we want is a simple, non-super-expensive, way to have security where and when we need it. Until Let’s Encrypt gets its kick off later this year (and probably for another year after that), it’s complicated and expensive to set up shared hosts with certificates, even if you use SNI.

    Enter CloudFlare and their bold proclamation that they’re going to provide free One-Click SSL for everyone, even their free-plan users. This is great! Except that it doesn’t work quite right.

    First off, if you use the flexible SSL plan, the one that doesn’t change your URL to HTTPS, then you need to use a plugin line CloudFlare Flexible SSL. Or you can just toss this into your wp-config.php:

    #SSL
    if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
    	$_SERVER['HTTPS']='on';
    } else {
    	$_SERVER['HTTPS'] = false;
    }
    

    Secondly, if you’re not actually changing the URL to SSL, you’ve got a problem. I don’t want my users to hit https all the time, not for all my sites. And all the reasons we don’t use https everywhere aside, it’s something to keep my running costs down.

    My rule is that SSL should be used anywhere where you are transmitting information that should not be public, and this suddenly was an issue when I looked at the levels of security. The one I’d want would be Full SSL, as it “Encrypts the connection between your site visitors and CloudFlare, and from CloudFlare to your server.”

    And there are two types of Full SSL, one of which is ‘Strict’ and requires you to have your own SSL cert, be it purchased or self-signed.

    CloudFlare SSL for everything? Not exactly: Full SSL (strict) is too strict.

    The obvious implications here, however, are that everyone would see HTTPS in the URL, and I don’t really want that. Of course, that’s not what they meant. What they mean is that IF you use https in the domain, then CloudFlare double encrypts. Otherwise it remains http for the domain.

    So basically normal SSL. And this is what I want, because I do have a purchased SSL cert for the domain in question and I do want to be secure all down the line. If I was using a self-signed certificate, I’d use the Full SSL (not strict) and that would work as well.

    One important thing to keep in mind is that if you chose to use the Flexible SSL, you’re not giving yourself login protection! As they point out, this gives you an encrypted connection between your site visitors and CloudFlare, but not from CloudFlare to your server. This greatly reduces the possibility of being sniped or sniffed, but the content from CF to use isn’t secured, which means if you use Flexible SSL for your store, you’re a moron.

    With that in mind, what good is it? Well you can be promised security throughput for your domain, and that, if you’re using CloudFlare, is a great thing.

  • Slow Site Troubleshooting: Database Edition

    Slow Site Troubleshooting: Database Edition

    So your WordPress is slow and you’ve already done the needful. You’ve checked your plugins and themes, you’ve put caching in place, you’ve checked for hacks, but it’s still slow, especially on the back end of WordPress?

    It may be your database.

    More specifically it may be your wp_options table. When your options table gets very, very large, it gets very, very slow. WordPress regularly queries that when you’re logged in, and it’s not indexed. DB indexes are used to locate data fast, without searching every row in the tables. This sounds sensible in many ways, but we don’t

    Indexes are used to quickly locate data without having to search every row in a database table every time a database table is accessed. Indexes can be created using one or more columns of a database table, providing the basis for both rapid random lookups and efficient access of ordered records. We don’t use ’em in the options table for a variety of reasons, but mostly that it slows things down.

    So in lieu of making indexes on your own, what can and should you do to debug things?

    Optimize Your Database

    Got WP-CLI?

    wp db optimize
    

    Otherwise you can use phpMyAdmin to check tables with overhead and clean ’em up. I don’t use a plugin, I think asking WP to optimize itself is a little weird, but you certainly can. The point here is to keep it clean.

    Cache that Database!

    Caching makes things better. Right? Kind of right. Mostly right. I use memcacheD (which I often typo as memecached) and that plus an object-cache.php file can cache your DB calls so they’re faster, which is great. Unless your wp_optimize table is too big.

    Your cache has two major issues. First, there’s going to be data you don’t want to cache (like private, sensitive information), but also when your cache is too big, or it’s trying to save data bigger than it is, it can make things slower by trying to cache, crashing, and repeating that over and over. That gets worse when we talk about the temp data generated by _transient entries in your database.

    Check The Size

    Your options table really shouldn’t be large. My biggest, busiest, site is only using 142K. It’s a site that’s old (8+ years now), it’s had many iterations and themes and plugins. You’d think it would be filthy with leftover code, because we all know plugins don’t always clean up. Nope. I did rebuild the DB once, in 2009, when I rebuilt the entire site from scratch, but that was 5 years ago and a lot has changed since then with plugins and themes. The next biggest site, a Multisite Network, has a wp_options of 100k. The biggest I’ve had is one of 500kb and that’s on a test site where I install and delete plugins daily.

    You get the point I trust. These things should be small. At most, I’m going to have a lot of _transient entries. But that’s actually issue here.

    Clean The Transients

    There’s a story here about why WordPress doesn’t really clean your transients. Why? Well as the name implies, transient data is meant to be transient. It should be temporary data, saved and used briefly, and then it should go away. A lot of developers, as it happens, were storing it for long term caches. Like checking when the last upgrade ran, or when a cron kicked off. So while for one, glorious, month we did nuke them all on upgrade, now we only delete expired transients, which doesn’t help as much as it could. As Nacin said:

    This leaves much to be desired, but we don’t want a core update to be blamed for breaking a site that incorrectly assumes transients aren’t transient.

    Basically people doing things wrong in a way we couldn’t adjust for.

    There are plugins like Delete Expired Transients and Transient Cleaner (which has the cutest header image). Those can be used to clean out your old transients.

    If you want to go whole hog, there’s a command to clean the whole thing out with SQL:

    DELETE FROM `wp_options` WHERE `option_name` LIKE ('%\_transient\_%')
    

    Of course you want to run a db optimize afterwards to actually flush out the rows.

    As always WP-CLI has features like wp transient delete-expired and wp transient delete-all.

    What if that doesn’t help?

    Then you should check the Database to see exactly what the biggest value is. In this case, I ran wp db cli to leap into the database and then this:

    mysql> SELECT option_name , length (option_value) AS blah FROM wp_options ORDER BY blah DESC LIMIT 5;
    +--------------------------------------------------+----------+
    | option_name                                      | blah     |
    +--------------------------------------------------+----------+
    | cron                                             | 12194468 |
    | _transient_feed_d117b5738fbd35bd8c0391cda1f2b5d9 |   223838 |
    | _transient_feed_ac0b00fe65abe10e0c5b588f3ed8c7ca |    98184 |
    | _transient_is_cached_instagram_images_self       |    97315 |
    | mytheme_storage                                  |    18354 |
    +--------------------------------------------------+----------+
    10 rows in set (0.02 sec)
    

    CRON is 12 megs. That would be the problem. Of course the only way I know of to fix that would be to totally trash cron and let it start over.

    Is That It?

    When you see a ginormous wp_options table, what do you do?

  • Mailbag: Debugging the Dread “No Permissions”

    Mailbag: Debugging the Dread “No Permissions”

    Sometimes customers hunt me up here. That’s okay. I’d rather you opened a ticket since I like to eat, sleep, and hang out with my wife, but in this case they also had an interesting problem.

    When I log in, I get “You do not have sufficient permissions to access this page.”

    I found the support ticket and solved it, and then I mailed the DreamPress Ninja’s with this methodology. It relies on wp-cli, so bear with me.

    The first thing to do is check the user list:

    user@server:~/example.com$ wp user list
    +----+-----------------+--------------------------------+-----------------------------+---------------------+---------------+
    | ID | user_login      | display_name                   | user_email                  | user_registered     | roles         |
    +----+-----------------+--------------------------------+-----------------------------+---------------------+---------------+
    | 2  | bobby           | Bobby Done Nightly             | bobby@example.com           | 2014-02-12 17:44:28 | administrator |
    | 3  | darren          | Darren Done Rightly            | darren@example.com          | 2014-09-29 17:49:11 | contributor   |
    | 4  | ethan           | Ethan Done Wrongly             | ethan@example.com           | 2014-10-27 21:01:07 | subscriber    |
    | 5  | jimmybear       | Jimmy Eaten By A Bear          | jimmy@example.com           | 2013-12-16 14:45:18 | author        |
    | 1  | iamempty        | Admin Account                  | admin@example.com           | 2015-03-05 01:30:09 |               |
    | 6  | sonnyboy        | Sonny Boyd                     | sonny@example.com           | 2014-10-27 22:13:08 | contributor   |
    +----+-----------------+--------------------------------+-----------------------------+---------------------+---------------+
    

    Notice how imaginet has NO role? That’s the problem. So let’s give it a role!

    user@server:~/example.com$ wp user add-role 1 administrator
    Success: Added 'administrator' role for imaginet (1).
    

    Check again and all is happy!

    user@server:~/example.com$ wp user list
    +----+-----------------+--------------------------------+-----------------------------+---------------------+---------------+
    | ID | user_login      | display_name                   | user_email                  | user_registered     | roles         |
    +----+-----------------+--------------------------------+-----------------------------+---------------------+---------------+
    | 2  | bobby           | Bobby Done Nightly             | bobby@example.com           | 2014-02-12 17:44:28 | administrator |
    | 3  | darren          | Darren Done Rightly            | darren@example.com          | 2014-09-29 17:49:11 | contributor   |
    | 4  | ethan           | Ethan Done Wrongly             | ethan@example.com           | 2014-10-27 21:01:07 | subscriber    |
    | 5  | jimmybear       | Jimmy Eaten By A Bear          | jimmy@example.com           | 2013-12-16 14:45:18 | author        |
    | 1  | iamempty        | Admin Account                  | admin@example.com           | 2015-03-05 01:30:09 | administrator |
    | 6  | sonnyboy        | Sonny Boyd                     | sonny@example.com           | 2014-10-27 22:13:08 | contributor   |
    +----+-----------------+--------------------------------+-----------------------------+---------------------+---------------+
    

    If the roles had be totally empty, it’s a case where the database is looking for the ‘wrong’ table prefix and that’s a little messier. There, you’ll want to grab the table prefix from the wp-config.php file:

    $table_prefix  = 'wp_hsy671e_';
    

    Then go into the database and look at wp_hsy671e_options for a field called wp_hsy671e_user_roles – if you don’t see one, that’s the problem. Check for one named wp_user_roles and rename it.

    If you DO see the right user_roles, then the problem is all the users are pointing to the wrong table. You can just run wp search-replace wp_user_roles wp_hsy671e_user_roles to force it back.