php shorthand if

I’ve known about the shorthand if for years:

<?php echo ($username) ? $username : ''; ?>

Today If saw this which is called Ternary and has been available since 5.3 which is older than my days of coding….

<?php echo ($username) ?: ''; ?>

Update PHP7.4
When assigning value especially in an array or object you can now do the following.

<?php

$person = [ 'date_of_birth' => '1970-01-01' ];

$person['name'] = $person['name'] ?? 'John Doe'; //php7.3
$person['name'] ??= 'John Doe';                  //php7.4

var_dump( $person );

for & foreach loops performance

Today we will handle a case of “Premature Optimization Is the Root of All Evil“.
But this is my blog and I was working with a very big api set, and will only get bigger, so (premature) thinking about memory usage and execution time might be a good idea in the long run.

Before I started this I thought that a for loop was faster then a foreach loop. And I usually pick foreach because it’s easier to write and read.

A quick google lands on a stackoverflow question which concludes the opposite. So I started to test a bit.

There is a big difference in my use case here.

I need to remove array items that need to be excluded from the api results. Most examples you will find online are about editing items.

First tests where quite clear, using a foreach was in most configurations faster than for. The test array I create has 10000 items and every 3rth item should be excluded:

<?php
$test_array = array();
for ( $i = 0; $i <= 10000; $i ++ ) {
    $test_array[] = [
        'index'   => $i,
        'include' => ( $i % 3 === 0 ) ? true : false,
    ];
}

The traditional foreach loop

The way I have been filtering arrays for years.
Create a empty array and only put in the elements the need to be included.

<?php
$filtered_array = [];
foreach ( $test_array as $item ) {
    if ( true === $item['include'] ) {
        $filtered_array[] = $item;
    }
}
return $filtered_array;

Immediately delete item

Don’t use a between array, just unset the item in the parent array

<?php
foreach ( $test_array as $key => $item ) {
    if ( true === $item['include'] ) {
        unset( $test_array[ $key ] );
    }
}
return $test_array;

Traditional pass by reference

The same as before, but the $item is passed by reference.
This is the big difference, since we are not editing the $item we want to remove it from the parent array

<?php
$filtered_array = [];
foreach ( $test_array as &$item ) {
    if ( true === $item['include'] ) {
        $filtered_array[] = $item;
    }
}
return $filtered_array;

Immediately delete pass by reference

Again the same and again passed by reference

<?php
foreach ( $test_array as $key => &$item ) {
    if ( true === $item['include'] ) {
        unset( $test_array[ $key ] );
    }
}
return $test_array;

The for loop

And last the for loop I was wondering about

<?php
$length = count( $test_array );
for ( $i = 0; $i < $length; $i ++ ) {
    if ( false === $test_array[ $i ]['include'] ) {
        unset( $test_array[ $i ] );
    }
}
return $test_array;

the results

I ran each of these loops 5000 times and measured the total time that took.
This was to insure the time between results was big enough to exclude the randomness (at least enough)
The test code I ran

Speed of Loops
  1. 5.6122910976414sec Loop: foreach traditional
  2. 6.0467801094055sec Loop: foreach unset key
  3. 7.7878839969635sec Loop: foreach traditional pass by reference
  4. 7.0686309337616sec Loop: foreach unset key pass by reference
  5. 8.6388339996338sec Loop: for

The traditional foreach I’ve been using for years turned out to be the fastest.
Research hours well spend ?

Bonus edit array item

As said before most examples use editing a array.
So I also ran that scenario. Test code
I upped the loops from 5000 runs to 7500 because the difference was so small.
And still it’s close.

  1. 15.363855123523sec Loop: foreach traditional
  2. 10.987272024155sec Loop: foreach foreach edit array directly
  3. 11.358484983444sec Loop: foreach traditional by reference
  4. 14.363346099854sec Loop: for

Here the traditional is the slowest. Reference is a lot faster as most articles claim.
But editing the array directly was the fastest.

WordPress filters and anonymous functions

Anonymous functions have been around for a long time. And since WordPress now supports php 5.6 it can be safely used.
And appears to be allowed?

Personally I’m not a fan of anonymous functions in combination with WordPress actions and filters. My main concern is you can’t remove them once registered. How ever today I found a use case which was very usefull in combination with the use

My example:

<?php
/* Template name: some-template */

// Gather all data
$condition_for_title = true;
$h1_title_override = 'very heavy and complicated check';
// the H1 also needed as the <title>

add_filter( 'pre_get_document_title', function( $title ) use ($h1_title_override, $condition_for_title) {
    if ($condition_for_title) {
        return $h1_title_override;
    }
    return $title;
}, 20, 1 );

get_header();

// start body
?>
    <h1><?php echo $h1_title_override ?>

Here I pass 2 variables h1_title_override and $condition_for_title which are created outside the function. In my case these where quite complicated and heavy checks. Of course I could put those in a function and cache the result. And call that check in the filter function. But still I need to check the current template before doing the function.

More traditional Example:

in functions.php

<?php
function complicated_check() {
    // Gather all data
    $condition_for_title = true;
    $h1_title_override   = 'very heavy and complicated check';

    return [
        'condition_for_title' => $condition_for_title,
        'h1_title_override'   => $h1_title_override,
    ];
}

function title_exception_for_template( $title ) {
    if ( ! is_page_template('clean-template.php')) {
        return $title;
    }

    $template_data = complicated_check();

    if ( $template_data['condition_for_title'] ) {
        return $template_data['h1_title_override'];
    }

    return $title;
}

add_filter( 'pre_get_document_title', 'title_exception_for_template', 20, 1 );

in clean-template.php

<?php
/* Template name: clean-template */
$template_data = complicated_check();

get_header();

// start body
?>
    <h1><?php echo $template_data['h1_title_override'] ?>

Both these approaches do the same thing. But the more traditional way is a lot more code. Although it has cleaner template.
I probably won’t use this much. If the anonymous function was more complicated it will get hard to read.

But for this case I think it was neat that I could use this little feature.

WP-cli run command over each subsite

If you use WP-cli command on a multisite it be default will only run on the mainsite.
But often you want to change a setting for all the sites.
In my case I wanted to set the timezone to Amsterdam for the whole network. That’s not hard:

wp option set timezone_string 'Europe/Amsterdam'

On a multisite this is a bit more difficult. But the script below will do the same for each site in a multisite.

wp site list --field=url | xargs -I % sh -c 'printf "SITE: %\n"; wp option set timezone_string 'Europe/Amsterdam' --url=%'

It consists of 3 parts.
First create a list of all site url’s

wp site list --field=url

Secondly we pass that on to xargs.
xargs is a very powerfull tool. One that I hardly understand and should go into deeper one day.
This is the best tutorial I found if you want to start with xargs.

The only thing important now is the -I %. This sets the variable to %.
But the most important thing here is that inside the '***' You can run any command. like normal.

xargs -I % sh -c '***'

Which brings us to the final part.
First print the site url on a line, then do the actual command we want to do on each sub site. As you can see we pass on the --url=% where we set the variable given in xargs.

printf "SITE: %\n"; wp option set timezone_string 'Europe/Amsterdam' --url=%

VVV disable backups

Update VVV 3.0

As of the release of VVV 3.0 this can all be done by adding the following in the vvv-custom.yml

general:
  # Backup the databases to the database/backups subfolder on halt/suspend/destroy, set to false to disable
  db_backup: false
  # Import the databases if they're missing from backups
  db_restore: false
list of vvv custom action files

Pre VVV 3.0

VVV is great but if you have 20+ sites in it most of which are quite big doing a reload or halt can be quite slow.
Disabling it isn’t the easiest. In vagrant-root/config/homebin/ create these 3 files:

  • vagrant_destroy_custom
  • vagrant_halt_custom
  • vagrant_suspend_custom

I wish there was a way to easily disable these. The vvv-custom.yml would be great for this.
Also adding a way to exclude specific databases.

source

php array; insert new item at specific index

The php function array_splice can be used to insert new items. At specific places.

<?php
$breadcrumbs = [
    'home',
    'year',
    'month',
    'day',
];
$new_crumb = [
    'category'
];
array_splice($breadcrumbs, 1, 0, $new_crumb);

The result is that category is inserted at the second place.

<?php
array (
    0 => 'home',
    1 => 'category',
    2 => 'year',
    3 => 'month',
    4 => 'day',
);

Running php from command line

This is one of those “I know this is possible, but don’t know how”.

php -r '$parts = [ "hello", "world" ]; echo implode( " ", $parts ) . PHP_EOL;' # hello world

Only useful for simple onelines. You’re very likely to be better of putting code in a php file and run that script like:

php ./helloworld.php

Run PHP with WordPress loaded.

Of course your good old friend wp-cli can help.
It can run code with wp fully loaded. So if you add things in the init action or even after the wp_loaded, those plugin/theme functions, posttypes and such are all available.

First off the plain php code execution, with wp eval

wp eval 'var_dump(did_action( "wp_loaded" ));'  # int(1)

Secondly we execute a file with WordPress fully loaded using wp eval-file

wp eval-file ./helloworld.php

Curl output format

A lot of usefull informatoin can be caught using curl. Formating the output can be done with the -w --write-out argument

curl -w 'Home loadtime: %{time_total}\nResponse code: %{http_code}\n' -o /dev/null -s 'https://www.janw.me/'
Bash output of curl request

Output example:

This example will show the load time and response code. These 2 values are duable. But If you want more it’s more readable to pass a file.

In file: nano curlformat.txt

    time_namelookup:  %{time_namelookup}\n
       time_connect:  %{time_connect}\n
    time_appconnect:  %{time_appconnect}\n
   time_pretransfer:  %{time_pretransfer}\n
      time_redirect:  %{time_redirect}\n
 time_starttransfer:  %{time_starttransfer}\n
                    ----------\n
         time_total:  %{time_total}\n

Then pass the file to curl:

curl -w "@curl-format.txt" -o /dev/null -s "https://janw.me/"
Bash output of curl format file

A list of all avialable variables are in the man pages

Sources:

Migrate one site out a multisite.

We where asked to host a site. But then it turned out that site was part of a bigger multisite. So after I got all files & db of the complete site I needed to clean it up and only keep the requested site left.

The quick and dirty steps:

  • Don’t change the DNS until you’re done testing.

Webhosting settings

  • Import all files on the server
  • Import the database and change the db settings in wp-config.php.
  • Locally add the domain to the hosts file So I didn’t need to change urls
  • delete all sites using wp site delete SITE_ID
  • Only keep site 1 (the primary you can’t delete) and site 12 (the site ID I needed)

Rename the tables

  • Rename the site 1 tables in mysql RENAME TABLE `amsterda_db`.`wp_links` TO `amsterda_db`.`wp_BACKUP_links`;
  • Rename the site 12 tables to the normal tables RENAME TABLE `amsterda_db`.`wp_12_links` TO `amsterda_db`.`wp_links`;
  • Make sure you only rename the site tables, don’t rename wp_blogs and other multisite tables

Update the url

  • In wp_blogs delete the record of site 12 and change the name to the new main site in wp_blogs.domain. And rename the url inside wp_site.
  • Also cleanup the wp_blog_versions.
  • In wp-config.php change the constant DOMAIN_CURRENT_SITE
  • Search the database for all mentions of the old mainsite. This is a per case check what should be done.
    Links in posts are probably fine. The old url in the wp_options maybe not.

Permissions and roles

  • In the wp_options table search for wp_user_roles as option_name. If this record is not present. Search it in the wp_BACKUP_options and create the record manually.
  • Permissions are still set for site 12 fix that using wp search-replace wp_12_ wp_ wp_usermeta

Test

  • Test your site. At least test uploads, permalinks, creating/updating pages
  • The normal steps usually when deploying a new site.
  • Think about things that might differ on this inherent setup.

Live

  • switch DNS
  • SSL

After the first week

  • Delete all wp_BACKUP_* tables
  • Check for unneeded plugins
  • Check for unneeded users.
  • Possible you should cleanup the uploads folder of old sites, although there may be links to those uploads.

Of course it would be even better to remove the multisite setup. But I was out of time. And I still would have done these steps first.