Half-Elf on Tech

Thoughts From a Professional Lesbian

Tag: php

  • Linear Regressions in PHP

    Linear Regressions in PHP

    Sometimes math exists to give me a headache.

    In calculating the deaths of queer females per year, my wife wondered what the trend was, other than “Holy sweat socks, it’s going up!” That’s called a ‘trendline’ which is really just a linear regression. I knew I needed a simple linear regression model and I knew what the formula was. Multiple the slope by the X axis value, and add the intercept (which is often a negative number), and you will calculate the points needed.

    Using Google Docs to generate a trend line is easy. Enter the data and tell it to make a trend line. Using PHP to do this is a bit messier. I use Chart.js to generate my stats into pretty graphs, and while it gives me a lot of flexibility, it does not make the math easy.

    I have an array of data for the years and the number of death per year. That’s the easy stuff. As of version 2.0 of Chart.js, you can stack charts, which lets me run two lines on top of each other like this:

    var myChart = new Chart(ctx, {
        type: 'bar',
        data: {
            labels: ['Item 1', 'Item 2', 'Item 3'],
            datasets: [
                {
                    type: 'line',
                    label: 'Line Number One',
                    data: [10, 20, 30],
                },
                {
                    type: 'line',
                    label: 'Line Number Two',
                    data: [30, 20, 10],
                }
            ]
        }
    });
    

    But. Having the data doesn’t mean I know how to properly generate the trend. What I needed was the most basic formula solved: y = x(slope) + intercept and little more. Generating the slope an intercept are the annoying part.

    For example, slope is (NΣXY - (ΣX)(ΣY)) / (NΣX2 - (ΣX)2) where,

    • x and y are the variables.
    • b = The slope of the regression line
    • a = The intercept point of the regression line and the y axis.
    • N = Number of values or elements
    • X = First Score
    • Y = Second Score
    • ΣXY = Sum of the product of first and Second Scores
    • ΣX = Sum of First Scores
    • ΣY = Sum of Second Scores
    • ΣX2 = Sum of square First Scores

    If that made your head hurt, here’s the PHP to calculate it (thanks to Richard Thome ):

    	function linear_regression( $x, $y ) {
    
    		$n     = count($x);     // number of items in the array
    		$x_sum = array_sum($x); // sum of all X values
    		$y_sum = array_sum($y); // sum of all Y values
    
    		$xx_sum = 0;
    		$xy_sum = 0;
    
    		for($i = 0; $i < $n; $i++) {
    			$xy_sum += ( $x[$i]*$y[$i] );
    			$xx_sum += ( $x[$i]*$x[$i] );
    		}
    
    		// Slope
    		$slope = ( ( $n * $xy_sum ) - ( $x_sum * $y_sum ) ) / ( ( $n * $xx_sum ) - ( $x_sum * $x_sum ) );
    
    		// calculate intercept
    		$intercept = ( $y_sum - ( $slope * $x_sum ) ) / $n;
    
    		return array( 
    			'slope'     => $slope,
    			'intercept' => $intercept,
    		);
    	}
    

    That spits out an array with two numbers, which I can plunk into my much more simple equation and, in this case, echo out the data point for each item:

    foreach ( $array as $item ) {
         $number = ( $trendarray['slope'] * $item['name'] ) + $trendarray['intercept'];
         $number = ( $number <= 0 )? 0 : $number;
         echo '"'.$number.'", ';
    }
    

    And yes. This works.

    Trendlines and Death

  • PHP Ternary Operations

    PHP Ternary Operations

    I mentioned a few days ago that I finally got to use ternary operations in my code and I was excited.

    I have a tendency to write my code out long form, which stems from how I actually sit down and think about my code. For example, when I need to check if specific parameters are set, I write it out like this:

    If variable X is equal to FOO, then do BLAH. Else do DEFAULT!

    When I translate that to PHP code, it becomes this:

    if ( $variable == 'FOO' ) {
        BLAH;
    } else {
        DEFAULT;
    }
    

    There’s nothing particularly wrong with it, but it can get a little long. And then consider code like this:

    if($_POST['variable'] == 'no' || $_POST['variable'] == 'yes'){              
        $variable = $_POST['variable'] ;
    }
    

    That has two issues. The first is that I’m trusting my data a little more than I should and not sanitizing when I save the variable. The second is I’m not validating smartly. If I want to be smarter, I do this:

    if( $_POST['variable'] == 'yes'){              
        $variable = 'yes ;
    } else {
        $variable = 'no';
    }
    

    At this point, I’m no longer allowing my code to be anything but yes or no, and if someone puts in ‘maybe’ as a value for the POST data, that’s nice but I’m forcing no.

    Another alternate way is this:

    $variable = 'no';
    if( $_POST['variable'] == 'yes'){              
        $variable = 'yes ;
    }
    

    There I default to ‘no’ and only pick ‘yes’ if it’s set to yes. This code is somewhat better because it only has one check and it’s obvious what my default is.

    But we can be even smarter! The trick is that I have to rethink how I’m checking for things. If all I care about is if a comparison is true or false, and I want to save a specific value to a variable based on that, then I can do my code all in one line:

    $variable = ( $_POST['variable'] == 'yes' )? 'yes' : 'no';
    

    This is the magic of the ternary operators. With it I get one line that checks for a specific result and defaults when that’s not it. You can make it more complicated, as long as the result is true/false. Like here:

    $schema = ( is_ssl() || get_setting( 'plugin-force-ssl' ) )? 'https' : 'http';
    

    Obviously they won’t work in all situations, and there are some cases where the two possibilities are going to make the code harder to read, but they can be downright nifty if you do them right.

  • EasyApache 4, PHP, and WP-CLI

    EasyApache 4, PHP, and WP-CLI

    Way back in March, I upgraded my server to use EasyApache 4. I did this because I wanted to have multiple versions of PHP on my server and be able to segregate it by domain. I also wanted to be able to upgrade PHP without having to rebuild Apache.

    For the most part, it’s been great. There were a couple odd snafus, like when it accidentally installed the wrong wp-cli on a minor upgrade for me, but it magically fixed it.

    PHP Breaks WP-CLI

    The problem was when I ran wp-cli commands, I got this error:

    PHP Warning:  array_slice() expects parameter 1 to be array, null given in phar:///usr/local/bin/wp/php/WP_CLI/Runner.php on line 610
    PHP Warning:  Invalid argument supplied for foreach() in phar:///usr/local/bin/wp/php/WP_CLI/Configurator.php on line 132
    PHP Warning:  proc_open(): Descriptor item must be either an array or a File-Handle in phar:///usr/local/bin/wp/php/commands/help.php on line 111
    PHP Warning:  proc_close() expects parameter 1 to be resource, boolean given in phar:///usr/local/bin/wp/php/commands/help.php on line 111
    Content-type: text/html; charset=UTF-8
    

    Scary! I tried reinstalling and that didn’t work

    I took a note of what version of PHP I had running on command line and so something weird:

    PHP 7.0.10 (cgi-fcgi) (built: Aug 22 2016 20:34:53) Copyright (c) 1997-2016 The PHP Group Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies with Zend OPcache v7.0.10, Copyright (c) 1999-2016, by Zend Technologies

    The Wrong PHP

    PHP 7.0.10 (cgi-fcgi) sounds like it should be okay, except I knew two thing:

    1. Fast CGI (fcgi) isn’t supported on EA 4 yet. It’s not even supported today. They’re working on it but it shouldn’t have been a thing installed.
    2. That should be cli not cgi-fcgi!

    I pinged a friend at cPanel who said “Oh no!” and promised to look into it for me. The next day I got an email that there was a minor PHP update to my server last night. I knew that meant PHP had been rebuilt. When I went to look, wp-cli was working again.

    Ergo – Something was wrong in PHP 7.0.10. It pushed the wrong version of PHP for command line somehow.

    Risk vs Reward

    Obviously this is a risk, having a server auto-update itself. It could install a rogue package or someone could typo or worse. At the same time, having PHP apply it’s own security patches for me means I don’t have to worry that I’m in Japan for a week and forget to do something. The risk, knowing that EA will roll out a fix for me, is less than the reward of being secure.

  • Updating Multiple Posts’ Meta

    Updating Multiple Posts’ Meta

    I had 328 posts that I needed to add a meta field value to. Thankfully they all had the same value at the moment, so what I really needed was to tell WP “For all posts in the custom post type of ‘show’ add the post_meta key of ‘tvtype’ with a value of ‘tvshow’.”

    That sounded simple. It wasn’t.

    Googling “Updating multiple posts” or “Bulk Update Multiple Posts” (with WordPress in there) was frustratingly vague and told me to do things like the bulk edit from the post lists page. Well. Sure. If I added my post meta to the bulk editor (which I do know how to do) and felt like updating them 20 shows at a time, I could do that. Heck, I could make my page list 50 and not 20, and do it in 5 ‘cycles.’

    But that wasn’t what I wanted to do. No, I wanted to figure out how to do it faster forever, so that if I had to update 32,800 posts, I could do it in the least CPU intensive way.

    PHP

    If I was to do this in PHP, it would look like this:

    $ids = array(
    	'post_type' 	=> 'post_type_shows',
    	'numberposts'	=> -1,
    	'post_status'	=> array('publish', 'pending', 'draft', 'future'),
    );
    
    
    foreach ($ids as $id){
        add_post_meta( $id, 'tvtype', 'tvshow' );
    }
    

    I picked add_post_meta instead of update_ because while the update will add the meta if it’s not found, I didn’t want to update any of the shows I’d manually fiddled with already. And to run this, I’d have to put it in an MU plugin and delete it when I was done.

    Which… Yes. That could work. I’d want to wrap it around a user capability check to make sure it didn’t run indefinitely, but it would work.

    WP-CLI

    Doesn’t a nice command line call sound better, though? Spoiler alert: It’s not.

    I knew I could get a list of the IDs with this:

    $ wp post list --post_type=post_type_shows --fields=ID --format=ids
    

    That gave me a space-separated list

    And I knew I could add the meta like this for each show:

    $ wp post meta add 123 tvtype tvshow
    

    But who wants to do that 328 times?

    The documentation for wp post meta update said “Update a meta field.” A. Singular. Now it was possible that this could be for multiple posts, since the information on wp post update said “Update one or more posts” and “one or more” means one or more. But the example only had this:

    $ wp post update 123 --post_name=something --post_status=draft
    

    Notice how there’s no mention of how one might handle multiple posts? In light of clear documentation, I checked what the code was doing. For the update function, I found this:

    	public function update( $args, $assoc_args ) {
    		foreach( $args as $key => $arg ) {
    			if ( is_numeric( $arg ) ) {
    				continue;
    			}
    

    The check for if ( is_numeric( $arg ) ) is the magic there. It says “If this is an ID, keep going.” And no spaces. So the answer to “How do I update multiple posts?” is this:

    $ wp post update 123 124 125 --post_name=something --post_status=draft
    

    Great! So can I do that with post meta? Would this work?

    $ wp post meta add 123 124 125 tvtype tvshow
    

    Answer: No.

    So I took that list, used search/replace to turn it into 328 separate commands, and pasted them in (in 50 line chunks) to my terminal to update everything.

    Yaaaay.

  • Mobile Ad Detection

    Mobile Ad Detection

    I screwed up not that long ago.

    I got an email from Google Adsense telling me that one of my sites was in violation because it was showing two ads on the same mobile screen, which is not allowed. Until I started using some of Googles whole page on mobile ads (an experiment), this was never an issue. Now it was. Now I had to fix it.

    Thankfully I knew the simpliest answer would be to detect if a page was mobile and not display the ads. Among other things, I know that I hate too many ads on mobile. So all I wanted was to use the Google page level ads – one ad for the mobile page, easily dismissible. Therefore it would be best if I hide all but two other ads. One isn’t really an ad as much as an affiliate box, and one Google responsive ad.

    For my mobile detector, I went with MobileDetect, which is a lightweight PHP class. I picked it because I was already using PHP to determine what ads showed based on shortcodes so it was a logical choice.

    Now the way my simple code works is you can use a WordPress shortcode like [showads name="google-responsive"] and that calls a file, passing a parameter for name into the file to generate the ad via a mess of switches and sanitation. Really you can go to http://example.com/myads.php?name=leaderboard and it would show you the ad.

    The bare bones of the code looks like this:

    <?php
    
    require_once 'Mobile_Detect.php';
    $detect = new Mobile_Detect;
    
    $thisad = trim(strip_tags($_GET["name"]));
    $mobileads = array('google-responsive', 'affiliate-ad');
    
    // If it's mobile AND it's not in the array, bail.
    if ( $detect->isMobile() && !in_array($thisad, $mobileads) ) {
    	return;
    }
    
    echo '<div class="jf-adboxes '.$thisad.'">';
    
    switch ($thisad) {
    	case "half banner":
    		echo "the ad";
    		break;
    	case "line-buttons":
    		echo "the ad";
    		break;
    	default:
    		echo "Why are you here?";
    }
    
    echo '</div>';
    

    The secret sauce is that check for two things:

    1. Is the ad not one I’ve authorized for mobile?
    2. Is this mobile?

    Only if both are false will the script continue to run. It’s simple but I like to have things stop as soon as possible to make loading faster. There’s no css trickery to hide things via mobile size detection. It’s as simple, and as close to a binary check as I can make it.

  • EasyApache 4

    EasyApache 4

    While we use Ubuntu and Debian things at work, I have a CentOS server that is (as of today) on CentOS 7. Which means not only is it weird and uses yum, but it has cPanel and WHM installed.

    Over the last year or so, cPanel has been working on EasyApache 4 (EA 4). EasyApache is their … easy way to manage Apache and upgrade things. It’s an interface to help make things like your LAMP stack easier to comprehend, and it’s actually pretty nice. EA 3 has been around for a while, and it has serious limitations. Like you have to re-run EasyApache to upgrade PHP (always annoying), and you can’t have multiple versions of PHP. That means when you upgrade PHP to 5.6, everyone is upgraded to 5.6, and damned be the conflicts.

    This … was a problem.

    I have some older code that can use 5.6 but I’ve not been able to fully test it on 7. And I want 7. You don’t even know.

    The actual upgrade process is remarkably painless. I had two errors, one of which was my own doing (don’t try to use opcache if you don’t have opcache installed…) and one was math related. The majority of my settings ported over painlessly.

    AH00316: WARNING: MaxRequestWorkers of 40 is not an integer multiple of ThreadsPerChild of 25, decreasing to nearest multiple 25, for a maximum of 1 servers.

    Obviously the fix there was to change MaxRequestWorkers to 50.

    The interface itself is much nicer. It’s a modern design that is easy to look at with whitespace and tabbing that is natural. The only thing I didn’t like was I couldn’t tell it ‘Install PHP 7 and pick the same options as PHP 5.6 please.’ I ended up copying down what I’d selected for PHP 5.6 and then manually reproducing.

    The provisioning process took a little getting used to, after years of EA 3, but once I had used it a couple times, it felt natural. This can be said of all things. Embrace change, right? It’s also faster, which is very welcome. The longest part is the review process, where it checks for conflicts. This used to happen ‘inline’ with the old deploy, so moving it out just changes the when. I found I preferred it, since I didn’t think “And I’m done!” I knew I couldn’t go forward.

    Initially it installed suphp. I didn’t notice that’s what the default was and found all sorts of errors because of permissions. Errors like this:

    SoftException in Application.cpp:255: File "/home/public_html/index.php" is writeable by group
    SoftException in Application.cpp:613: Directory "/home/public_html/gallery" is writeable by group
    

    I actually knew the fix for this was to run the following in my public_html folder:

    find . -type f -name '*.php' -exec chmod 644 {} \;
    find . -type d -exec chmod 755 {} \;

    And yes, that solved everything. I was logged in as Ms. Groves root anyway, so I bounced through my users and ran it.

    But… did I want to stay on suphp? Turns out EA 4 only supports FastCGI with the FastCGI Process Manager (PHP-FPM), and while I’ve been using that for a while (instead of the dread mod_php). The problem there is that suPHP or CGI PHP handlers don’t support opcache. That meant I either had to suck it up and do the manual install for FastCGI or I could wait for v58 of WHM, which will have a UI.

    I opted to wait and stay on suPHP right now. The domains on my site are pretty fast, thanks to the SSD and a well optimized server. Load time did increase, I won’t lie, but it was pretty negligible. I don’t think it’ll really hurt things at this moment. I did some quick tests on GTmetrix and found my load time for sites on PHP 7 actually decreased.

    And that’s why I wanted PHP 7. It’s just faster.