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:
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
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
5.6122910976414sec Loop: foreach traditional
6.0467801094055sec Loop: foreach unset key
7.7878839969635sec Loop: foreach traditional pass by reference
7.0686309337616sec Loop: foreach unset key pass by reference
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.
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 );
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.
And finally reload nginx: sudo nginx -t && sudo service nginx reload
Download Selfoss
Next we download Selfoss. On the Selfoss home page there is a download button. Copy the destination of that link. At the time of writing it is on version 2.18 and the download zip is Replace the url if needed.
Now visit the url. You should see an empty -but working- selfoss screen.
Configuration
Although this installation works we are going co configure some aspects of it.
DB connection.
Be default Selfoss uses sqlite. Mysql has a better performance. Instructions to install Mysql are in a separate article. More detailed instruction are there. So here are the quick instructions to create a database.
Login to mysql as root: sudo mysql -uroot Create a db, and assign a new user. !Replace the password You can set your own database name and user.
CREATE DATABASE misc_selfoss;
CREATE USER 'selfoss'@'localhost' IDENTIFIED BY '%%SAFE_PASSWORD%%';
GRANT ALL PRIVILEGES ON `misc_selfoss`.* TO `selfoss`@`localhost`;
FLUSH PRIVILEGES;
EXIT;
Next we set the db connection in the config file: sudo nano config.ini
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=%
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
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.
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
In this guide we will install php 7.4 and do very basic tweaks. This should also work with php8 and older versions.
In this guide we will install php 7.4 and do very basic tweaks
Note: this post call also be used to install php7.0, php7.1, php7.2 or php7.3, just change every reference to 7.x. Only php7.3 and higher are still supported This guide should also work for installing php8.0 but I haven’t tested this yet.
Check if you can install php7.4
To check if php7.4 is available run: sudo apt install --dry-run php7.4 If you get an error continue, if you don’t get an error continue to actual install php
As the php version is not available We are going to create a new source for php. We do this with the following 3 commands.
When done check it with php -v it should show a php 7.4.0 or higher. Now we need to add a few fpm things for nginx to work properly. Create a extra config file sudo nano /etc/php/7.4/fpm/conf.d/90-pi-custom.ini. And add:
In this guide we are going to install Nginx. Configure it to work with php and give Nginx it’s own users so you don’t have to update permissions.
In this guide we are going to install a webserver (nginx). For those who are bit familiar with webservers you might wonder why we are not using apache? I myself are more familiar with Apache but Nginx just has better performance. And on the not so powerful pi that is important.
Install Nxing
To start we just plainly install nginx.
sudo apt install -y nginx
Check the version nginx -v it should be version nginx/1.14.2 or higher
To test if it works enter the IP address in the browser. You should see this page.
If you see an error most likely IPv6 isn’t supported. Open the nginx config file sudo nano /etc/nginx/sites-enabled/default. At the top you should see these lines
First we will do some global settings open: sudo nano /etc/nginx/conf.d/90-pi-custom.conf Add the following line, we increase the allowed upload sizes.
client_max_body_size 64M;
Note: for the example I use example.local replace that with the url you want to use. If you don’t have the url setup yet you can add it to you own host file. Or instead of the url use the IP-address of the Pi.
We will create our own vhost files. PS I’ll be using example.local but use whatever you intend to use. Create the vhost file: sudo nano /etc/nginx/sites-enabled/example.local.conf
In the main file add sudo nano /var/www/example.local/public_html/index.php
<?php
phpinfo();
To test the url example.local you need to add it to your host file. Depending if you’re on Windows, Mac or Linux the instructions are different. See this guide: How to edit your hostfile
Now go the the url and you should see this:
example phpinfo
Now we have a basic and functioning webserver with php. For security sake delete the index.php file
So far we use sudo for pretty much everything. This is fine. After the initial setup most of the work has to be done in /var/www/*. These files are owned by the www-data user. This is fine but it can be a bit annoying when editing files with a different user. Either the owner is the www-data user or it’s the user you use to login in (like the pi user).
So we’re going to create a new user assign it as the nginx user. Then we’lluse that user going onward to login as that user when we’ll want to change things to the sites.
We will not change it to the pi or ubuntu, because those users have sudo access. That’s a huge security risk.
Create a new user
Before we create the user we are going to check the current users to see if it does not already exists.
cat /etc/passwd
With that clear let’s create the user:
sudo adduser deployment
Fill in the password, the rest of the questions are optional.
Assign this user to Nginx
In /etc/nginx/nginx.conf on line #1 change the user.
#user www-data;
user deployment;
And change the user for php-fpm (If you’re using php-fpm ?)
Create a file in /etc/php/7.4/fpm/pool.d/ (check the php version) I suggest something like zzz-custom-user.conf so it’s loaded last. To that file add.
user = deployment
group = deployment
listen.owner = deployment
listen.group = deployment
Next reload everything, agian check php version.
sudo nginx -t && sudo service php7.4-fpm restart && sudo service nginx restart
Finally change the owner of the webfiles
sudo chown deployment:deployment /var/www/ -R
You should be done changeing the nginx user to deployment. Open a new terminal and try to login to your Pi using this newly created user: ssh deployment@192.168.2.111 It will ask for the password, but you should be able to login. If you want you can set you ssh key for the deployment user.
Also keep in mind if you had cronjobs on the previous user you should also move these to the new user. (We haven’t setup cronjobs in this guide).
You’re Nginx setup is now done and you are ready to add sites to it.