Half-Elf on Tech

Thoughts From a Professional Lesbian

Tag: development

  • Query Strings vs Static Resources

    Query Strings vs Static Resources

    If you ever do a page speed check on your site, you may have seen the notice to “Remove query strings from static resources.”

    What Are They And Why Do I Care?

    At their most basic, a query string is any URL that has a ? in it. For example, if you go to a url of https://example.com/?lang=en then that last bit is the query string. It’s job is to tell the browser to pass on specific information to the website. It’s essentially a variable that will be read by the website or the browser.

    A static resource is a file used by a website that doesn’t change much (if at all). These are usually images but also CSS (style sheets) and javascript files. These resources can be stored on web servers, proxy/CDN servers (like Cloudflare or MaxCDN), or even a web browser, which makes your sites run faster.

    However. The explanation is that browsers don’t cache anything with a query string. Which means, yes, if your static resources have one, they won’t get cached.

    Or will they? (It’s complicated.)

    Why Do My Resources Have Query Strings?

    If query strings break caching, why would anyone have them? Because we don’t always put version numbers in our paths for common files. For example, in WordPress if you call jQuery (a common javascript library), you’re calling a path like https://example.com/wp-includes/js/jquery/jquery.js and that has no version number. But WordPress has certainly updated that a number of times.

    In order to make sure that jQuery gets properly cached when you upgrade, WordPress adds a query variable like this: https://example.com/wp-includes/js/jquery/jquery.js?ver=1.12.4

    This is important because otherwise when you upgraded WordPress, you’d see a page formatted weirdly and misbehaving, all because the wrong (cached) jQuery was loaded.

    Should I Remove The Query Vars?

    Maybe.

    Yes, allowing your resources to be cached will make your site faster, and it will get you a higher score in those page speed checks. At the same time the speed benefit is pretty low compared to the dangers of those files not being properly updated. Features on your site will, literally, cease to function.

    So you’ll need to decide for yourself which is more important.

  • Is This A Good Idea?

    Is This A Good Idea?

    I’ve joked about this a few times, that I should go into business telling people if their web idea is a good one or not. The prices would be simple.

    • $25 – A quick yes or no.
    • $100 – I’ll tell you if it’s dangerous or possibly illegal.
    • $500 – Details of everything.

    While I doubt anyone would ever actually pay for that, let me tell you some things I think are bad ideas for the web, and why.

    Obvious Bad Ideas

    Excluding the whole “Facebook but for pets!” and “Uber but for Pizza!” ideas, and dismissing every single ‘disruptive’ concept out there (seriously, no they’re not), some ideas are really easy to point at and say ‘this is a bad idea.’

    If you’re thinking about making something new for WordPress, before you start coding, please use google. Because the first kind of bad idea is the idea that’s been done before, ad nasueum. For example, sliders, snowfalls, BMI calculations (actually ANY sort of calculators including loans), ‘simple’ contact forms, and Google Analytics.

    These are bad ideas because they are overdone.

    If it’s been done more than 10 times, and you’re not introducing something totally new (this includes the fellow who made a ‘login logo slider’ – no, it wasn’t new), then file it away as a good experiment. Write the code, but don’t publish it.

    Illegal/Dangerous Ideas

    Depending on how often you hear me rant, you may or may not be surprised to find out how often people write code that’s illegal.

    Now hold on. Before a single one of you says “But the GPL!” let me remind you. The GPL doesn’t make things magically legal just because it’s open source. You can use GPL code to break laws (like, say, make a child porn website), and while that’s fine for the GPL, it’s still illegal.

    On a less creepy but still illegal note, the Yahoo! finance APIs aren’t actually legal for you to use in your code. Yes, I am well aware of the number of people who make packages for it. I’ve actually spoken to Yahoo about this and the way they explained it was this. Their Finance API is for your personal use. You’re not meant to use it to retrieve data for apps (and yes, plugins are apps) or any third-party usage (again, plugins). Also they do make it pretty clear with this comment:

    By accessing the Yahoo! site, you agree not to redistribute the information found therein.

    A lot of public APIs have these restrictions, including Airbnb and even some Google APIs (finance again). And using them without checking the terms of use and verifying they’re allowed to be used in your situation puts you at risk for breaking the law and that is dangerous because, in the case of a plugin, it’s not just you who pays the price.

    Ignorant Code

    Really the magic of everything, the answer to all ‘is this a good idea’ questions can be found in this. Did you bother to do the research first? And no, I don’t mean did you do market research (though that’s a good idea too).

    Did you check if the idea existed already? Did you check if the tools you want to use permit that kind of use? Did you read the terms of use of any service? Did you listen to your gut or not?

    Think first. Look before you leap. And above all, please don’t make yet another snowflake tool. No one actually likes them.

  • Loading Common Libraries Smartly

    Loading Common Libraries Smartly

    When you’re writing code, there’s a desire to not reinvent the wheel. This is a good desire. If someone else has made the code you need, using it is a better use of your time. But there’s a right way and a wrong way to include those common libraries, especially in WordPress themes and plugins.

    PHP Is Dumb

    Okay, PHP isn’t dumb, but it’s a very top-down/left-right kind of language. The whole reason we tell people to name functions and classes uniquely in WordPress plugins and themes is that if two people name a function the same thing, PHP gets dumb and errors, telling you it can’t define the same function twice.

    This means that commonly used PHP libraries should also be uniquely named. To their credit, most do. But still, what happens if two WordPress plugins or themes include the same library? That same error comes back about not defining the same function twice!

    What this means is that when using a common library, it’s important that you enqueue it safely and smartly.

    Check If The Library Exists

    The magic is that you need to detect if the code is already included and not re-include it. PHP gives you two straightforward ways to do this: class_exists and function_exists.

    It’s much easier for me when the code is wrapped nicely in a class. Assuming you have a library file that includes the class but doesn’t call it, you can check like this:

    <?php
    if ( !class_exists( 'MyLibraryClass' ) ) {
        include( 'path/to/library.php' );
        $mylibraryclass = new MyLibraryClass();
    }
    

    If it does include the class (with a new MyLibraryClass() in the library) then you can leave that bit out.

    When you have a library that perhaps predates classes, you’ll want to pick the primary function and call it like this:

    <?php
    if ( !function_exists( 'my_library_function' ) ) {
        include( 'path/to/library.php' );
    }
    

    What About The Weird Stuff?

    There are two things I left out. What about namespaces and javascript libraries?

    First of all, it’s impossible to check for the existence of a namespace. You see, a namespace doesn’t really exist. What does exists are the structures within the namespace. That means if you wanted to check for the class ClassName in the namespace of MyName you have to do this:

    class_exists( '\MyName\ClassName') );
    

    Second, if you’re looking for javascript libraries, and you’re using WordPress, you don’t have to. Just enqueue it.

    Schnieder from One Day at a Time (2017) going 'Mind blown'

    The WordPress function wp_enqueue_script() registers the script but does not overwrite and enqueues it.

    Now there is a trick to all this. If you use the enqueue command properly, then this always works. By this I mean if you do it this way, it’ll work:

    wp_enqueue_script ( 'chartjs', 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.bundle.min.js' );
    

    But… A lot of people do this:

    wp_enqueue_script ( 'my-plugin-chartjs', 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.bundle.min.js' );
    

    And that means WordPress will load both chartjs and my-plugin-chartjs which sucks.

    Also you don’t need to append -js (or -css) to your enqueues, as WordPress will do that for you.

  • You Don’t Know Paths

    You Don’t Know Paths

    A common issue with plugins is where people insist they must call wp-load.php directly so that they can call WordPress functions and data when WordPress isn’t loaded. When I push back on that code, the common rationale is that there’s an external service that needs to ping a reliable URL to run code.

    You Don’t Know Where WordPress Is

    When you hardcode in paths, or assume that everyone has WordPress in the root of their domain, you cause anyone using ‘Giving WordPress it’s own directory’ (a very common setup) to break. In addition, WordPress allows users to change the name of the wp-content folder, so any plugin that tries to guess where it is would break on any site that choses to do so.

    This cuts both ways. First your clever script that tries to go to example.com/wp-content/plugins/your-plugin/yourfile.php is going to fail if someone moves that folder. Second your clever file that looks for wp-load.php by going up a couple folders will also fail.

    You can’t know where WordPress is. The only way to know all that would be to do things from within WordPress.

    Never Call Files Directly

    The only files you should ever call directly in your code is your code’s files or if you’re checking for the existence of another plugin or theme. This is the only time it’s smart to be hardcoding in paths of other code directly into your code.

    if ( is_plugin_active( 'plugin-directory/plugin-file.php' ) ) { } 
    

    Since most people call these files to have access to WordPress ‘without loading WordPress,’ the actual correct fix is to tie processing functions (the ones that need but don’t have access to core functions) into an action hook, such as “init” or “admin_init”.

    Code you add to WordPress should be inside WordPress, only accessible to people who are logged in and authorized, if it needs that kind of access. Plugin’s pages should be called via the dashboard like all the other settings panels, and in that way, they’ll always have access to WordPress functions.

    What’s The Right Answer?

    Okay. There’s one case where it makes sense that you might possibly need to call a URL directly. If you’re hooking WordPress up to a service and you want to have a good URL to call, then yes, you would be well served by having one of the following:

    • example.com/?myplugin=runstuff
    • example.com/myplugin/

    Don’t those look nice? There are two main ways to get there.

    External Rules

    An external rule is basically you adding an redirect rule for a URL to point to a file. You could do this in .htaccess or nginx.conf but if you do it in WordPress, it looks like this:

    add_action( 'init', 'myplugin_external_rules' );
    function myplugin_external_rules() {
        global $wp_rewrite;
        $plugin_url = plugins_url( 'yourfile.php', __FILE__ );
        $plugin_url = substr( $plugin_url, strlen( home_url() ) + 1 );
        $wp_rewrite->add_external_rule( 'myplugin$', $plugin_url );
    }
    

    Now the obvious issue here is that you’re still calling a php file directly, which means you don’t get any access to WordPress functions. Boo. Thankfully there’s another answer.

    Internal Rules

    While more complicated and weird, this is a better solution.

    First you need to actually make the rule:

    add_action( 'init', 'myplugin_internal_rules' );
    function myplugin_internal_rules() {
        add_rewrite_rule( 'myplugin', 'index.php?myplugin=runstuff', 'top' );
    }
    

    This means that those two example URLs I pitched before are the same thing.

    Next you need to tell WordPress not to stomp all over the query variables:

    add_filter( 'query_vars', 'myplugin_query_vars' );
    function myplugin_query_vars( $query_vars ) {
        $query_vars[] = 'myplugin';
        return $query_vars;
    }
    

    You have to do this because otherwise WordPress won’t know what to do with the query variable.

    Finally we tell WordPress what to actually do with your variable:

    add_action( 'parse_request', 'myplugin_parse_request' );
    function myplugin_parse_request( &$wp ) {
        if ( array_key_exists( 'myplugin', $wp->query_vars ) ) {
            include 'yourfile.php';
            // Call your functions here!
            // function_name_thingy();
            exit();
        }
        return;
    }
    

    You’ll notice that // your functions here code is waiting for your code. That’s because this time we’re including the PHP file and then in there you’ve theoretically written your code in a nice little function. And that function? Uses WordPress functions because it is one.

  • Semi Related Posts

    Semi Related Posts

    Once in a while you write some code that you’re pretty sure will never be useful to anyone else on the planet, but the idea is cool, so you want to share it.

    Hence this post.

    The Power Of Relations

    I was making coffee one Sunday morning, thinking about how I could keep turning a site into a wiki-level click hole, where a person could get lost for hours and days reading article after article. The reason WikiPedia works so well is that the content is all interlinked. Now, if you’ve never been a wiki editor, and you’ve just noticed that there are a billion links on every page, taking you around the universe, I’ll let you in on a secret. Those links are all human-made. A person goes in and makes those links, giving you ‘related’ posts.

    But WikiPedia really only works well due to the massive amounts of people who know weird shit and are willing to help and share. When it’s just you and your crazy friend running a site, that’s a lot of work. And you have wives and pets and kids to take care of. No, you want– you need to automate the heck out of this stuff.

    Related Posts are Hard

    Making related posts is a hard thing. There are myriad algorithms out there to calculate ‘relatedness’ and they’re all crazy complex. Worse, they all require a lot of processing power. There’s a reason Jetpack’s related posts feature runs on their servers. They’ve got more power than you do, and it stops people on shared from being nasty neighbors.

    This code is not a replacement for that code. This code is very specific and very targeted. It relies on people being clever (but not too clever). But when it works, it makes the interwoven data sets of a site work well. Because you see, we’re not talking about ‘If you like this article, read this other one…’ No. No we’re talking about crossing custom post types.

    Relations Across Post Types

    Hold on. I know what you’re thinking.

    The point of separate post types is not to cross the streams. Yes, I know. But that’s actually not true. Menus, for example, are a post type, and you a great many people want to have specific menus to their post types. That sounds logical, doesn’t it? Well, in this case, I have a post type for TV Shows, and I have regular old blog posts.

    My lightbulb moment was three-fold:

    1. When we write articles about the TV Shows, we logically use tags of the show name.
    2. Those show name tags match the slugs of the TV shows.
    3. If we link back to the articles from the TV Show page, then we’re getting people down the click hole.

    This meant my logic was simple: On the TV show page, check if there are any blog posts tagged with the show name and, if so, that post was ‘related’ to the show and therefore we should link back to it.

    The Code

    I put all this into a function so I could re-use it in a couple places. I like not repeating myself with code. I also have the function call another function, because I actually re-use this to connect different post types that all use the same tags. If you don’t need that, you can tighten it up.

    The Display

    This is in the theme:

    $slug = get_post_field( 'post_name', get_post() );
    $term = term_exists( $slug , 'post_tag' );
    if ( $term !== 0 && $term !== null ) { ?>
    	<section name="related-posts" id="related-posts" class="shows-extras">
    		<h2>Related Posts</h2>
    		<?php echo related_posts( $slug, 'post' ); ?>
    	</section> <?php
    }
    

    The Functions

    And here’s the function it calls:

    function related_posts( $slug ) {
    	$related_post_loop  = related_posts_by_tag( 'post', $slug );
    	$related_post_query = wp_list_pluck( $related_post_loop->posts, 'ID' );
    
    	if ( $related_post_loop->have_posts() ) {
    		$the_related_posts = '<ul>';
    		foreach( $related_post_query as $related_post ) {
    			$the_related_posts .= '<li><a href="' . get_the_permalink( $related_post ) . '">' . get_the_title( $related_post ) . '</a> &mdash; ' . get_the_date( get_option( 'date_format' ), $related_post ) . '</li>';
    		}
    
    		$the_related_posts .= '</ul>';
    	}
    
    	return $the_related_posts;
    }
    

    And here’s the function that calls:

    function related_posts_by_tag( $post_type, $slug ) {
    	$term = term_exists( $slug, 'post_tag' );
    	if ( $term == 0 || $term == null ) return;
    
    	$count = '5';
    	$query = new WP_Query( array(
    		'post_type'       => $post_type,
    		'posts_per_page'  => $count,
    		'no_found_rows'   => true,
    		'post_status'     => array( 'publish' ),
    		'tag'             => $slug,
    		'orderby'         => 'date',
    		'order'           => 'DESC',
        ) );
    
    	wp_reset_query();
    	return $query;
    }
    

    The Result?

    An unordered list of the last five related posts. You could add a link to ‘show more’ if you were so included.

  • Git Attributes: Control Your Vendor Folders

    Git Attributes: Control Your Vendor Folders

    When you’re developing code, a lot of the time you have vendor or bower_component folders that you don’t need. That is, you don’t need them for the code, but you do for the development.

    A year ago I explained how I handle my vendor folders. Essentially, I use SVN to ignore things so they don’t get uploaded to WordPress. And that’s great but

    What about Git?

    Enter Git Attributes

    The .gitattributes file lets you define attributes for paths. What that means is you can assign an attribute to a file which will then impact how various Git operations occur.

    Operations are things like checking your code out or pushing a release (something familiar to Githubbers). Basically an operation is when Git does a ‘thing.’ Whatever that thing will be. And the Attributes file allows you to specify what happens to specific files (or folders) when that thing happens.

    How Do You Use It?

    There’s a lot more to it than this, but if your goal is to exclude vendor folders and .git files from your zips to send them off to people, then you’ll want to have your .gitattributes file look like this:

    /vendor/ export-ignore
    .gitattributes export-ignore  
    .gitignore export-ignore
    

    This results in when you use GitHub and tell someone to download the zip from it, it will exclude those files.

    What Else Can I Do With It?

    Have a problem with line endings because one person on your dev team uses Windows? gitattributes can help.

    Need to make a change like that for everyone on a system? Or maybe you want to make sure you never include those vendor and documentation folders? The Pro Git Book says this:

    Attributes for all users on a system should be placed in the $(prefix)/etc/gitattributes file.

    Before you ask, unless you changed it, $(prefix) is nothing for pretty much everyone. You may have a /usr/local/git/etc/ location though.