HTTP caching mechanisms in Caddy

Providing the ability for clients to cache static file requests is very important. It prevents the unneeded transfer of (static) files from server to client. Caching doesn’t only speedup things for the client, but also decreases the server load.

But which caching mechanisms does Caddy provide, and how can they be enabled?

ETag

The HTTP ETag or entity tag is a header which contains an assigned identifier to each resource. The identifier is specific for each version of the resources. That way the client can do conditional requests by means of knowing whether a resource has changed or not.

What should we do to enable it in Caddy?
Well… you mustn’t do anything. ETag support is built-in and works out of the box (on static files) with Caddy! (It is included since version 0.8.3.) ❤️Caddy!

Cache-Control

Another caching mechanism is the Cache-Control header field. This header field contains a value (in seconds) specifying how long the browser may cache a result (or whether it may even cache the request).

Caddy doesn’t have a directive for this, so I even started writing code for a middleware, which, after a few minutes of research, seemed completely useless, since we can easily add custom headers in the Caddyfile.

The Cache-Control values are fairly easy to understand and to use. For my WordPress blog I use these:

header /blog/wp-content/ Cache-Control "max-age=2592000"
header /blog/wp-includes/js/ Cache-Control "max-age=2592000"

Cache-Control is the name of the header field. max-age is the time interval (in seconds) for which the browser may cache the result. Other values of the Cache-Control header field:

  • public, max-age=2592000: responses may be cached by the client’s browser and all intermediary caches. The public directive is implicit so you don’t need to specify it.
  • private, max-age=2592000: responses may be cached by the client’s browser only
  • no-store: completely disable caching

Expires header

The expires header is outdated by the Cache-Control header. It contained the exact date on which the cache of a resource should expire. This value is thus set dynamically, which is something we can’t do with the header directive of Caddy.
But… In most modern systems the Cache-Control header is used instead of the expires header. So you can stick to the two above caching mechanisms.

Two months ago I started to “Serve The Web Like It’s 2016”, and I’m still loving Caddy!

Caddy?

Wooha, hold your horses, what is Caddy?! Caddy is a web server like Apache or Nginx, but different:

  • Caddy is a modern web server (supporting HTTP/2) with elegant and easy configuration. It comes with automated HTTPS (with Let’s Encrypt) and a growing number of extensions (including Git support).
  • It’s open source software, written in Go, and driven by an awesome community.
  • Well documented
  • Cross-platform: available for Linux, Mac OS X, FreeBSD, Windows, OpenBSD and Android.
  • All this in just one binary executable!

Caddy logoCaddyfile

Configuration for Caddy is done using a Caddyfile, which is a new language specifically for Caddy (ok, this seems odd at first sight). But by using a domain specific language, configuration becomes very simpel and to-the-point.

Caddy is very powerful (and easy at the same time), which I will demonstrate with Caddyfile snippets for some of my real-world situations.

Configuring Caddy can be very easy for most sites:

denbeke.be {
    root sites/denbeke.be
}

Look, I just created the config for a static website with a document root. This site is automatically served over HTTPS, and HTTP traffic is automatically redirected to HTTPS1.

Dynamic PHP websites are of course also supported. Simply use PHP-FPM as you would do with Nginx:

denbeke.be {
    root sites/denbeke
    fastcgi / unix:/var/run/php/php7.0-fpm.sock php
}

But Caddy is not only easy, it comes packed with a lot of features, which support a lot of use cases.

My personal WordPress blog runs perfectly with only 12 lines of code:

www.denbeke.be {
    redir https://denbeke.be{uri}
}

denbeke.be {

    root sites/denbeke
    log sites/logs/denbeke.be.access.log
    errors sites/logs/denbeke.be.error.log

    fastcgi / unix:/var/run/php/php7.0-fpm.sock php

    # Routing for WordPress
    rewrite /blog {
        to {path} {path}/ /blog/index.php?{query}
    }

    gzip
    header /blog/wp-content/ Cache-Control "max-age=2592000"
    header /blog/wp-includes/js/ Cache-Control "max-age=2592000"

}

I have a redirect from www.denbeke.be to denbeke.be, redirect from HTTP to HTTPS (note that non-HTTPS traffic is automatically redirected to HTTPS with Caddy by default1), error and access logging, and PHP with routing for WordPress. I also compress all content with gzip and set caching headers to optimize used bandwidth.

For my Git server, I have a reverse proxy to Gogs. Again, Caddy takes care of HTTPS… The Caddyfile for this is again trivial:

git.denbeke.be {
    proxy / http://localhost:3000
}

My last example is the website for a group project, where the complete website is put in a Git repo, so all contributors can edit the website. The following directive will clone the complete repo on initial setup, and pull changes every hour:

oakstrong.be {
    root sites/oakstrong
    git https://bitbucket.org/oakstrong/oakstrong.be.git
}

It’s all very easy, isn’t it?

And this is just a subset of Caddy’s capabilities, Caddy supports a lot more, like automated Hugo publishing (French blogpost), running Caddy on Android, or any other PHP CMS or static site generator. Caddy also has a growing number of add-ons/modules, which add a variety of features to Caddy, like Prometheus metrics, IP filtering, search, Cross Origin Resource Sharing, JSONP, …

Running Caddy as a service…

… is easy! (and others, including myself, already made configuration files and tutorials about it)

Caddy in production: rock solid!

But have you tried running Caddy in production? Is this new and young web server stable enough? Doesn’t it sometimes crash?
Simple answer Yes it’s stable, and No it hasn’t crashed yet.

Pingdom Uptime report Caddy webserver

At the beginning of February 2016 I made the switch from Nginx to Caddy (together with the switch from PHP 5 to PHP 7) on my home server and my production server. Two months later Caddy already served thousands of visitors on various websites.2 Caddy hasn’t crashed ever since, and I currently have 100% uptime on my production server.3

Does this mean that Caddy is perfect and bug-free? Of course not!
I started using Caddy in production after a stupid FastCGI bug was fixed (which caused requests to block). While moving to production I submitted a few pull requests and issues on GitHub to fix some important and less important bugs. But I can confirm Caddy now perfectly works with WordPress, Piwik, ownCloud, Vanilla Forums, SMF, and all self-written PHP stuff.

If you take a look at the issues, pull requests, or Gitter, you’ll quickly notice that Caddy is a very active project, in which bugs got fixed very swiftly by a growing community. Caddy currently has 67 70 contributors.

Conclusion

Caddy is awesome! And getting better every day!


  1. If you don’t want to use automated HTTPS certificates, you can specify your own certificates (or disable HTTPS) using the tls directive.
  2. I must admit that I’m not serving high traffic websites, but Matt Holt got this reply from ‘a guy’:

    I got this email from a user that has sometimes has ~100 concurrent users on his site (that uses PHP), and switched from nginx to Caddy:

    The server has been performing well and server resources are the most free that they’ve been in months.

  3. Something else to admit… right after I wrote this blogpost, my MySQL server crashed, so I can’t speak of a 100% uptime anymore. But this doesn’t change the fact that Caddy hasn’t caused any problems (yet).

Serving ownCloud with Caddy

In this post, I’ll walk you through how to set up ownCloud with Caddy for a secure, personal cloud service. I wrote this guide while configuring on Ubuntu 14.

ownCloud

A quick introduction to ownCloud for those who never heard about it (as found on Wikipedia):

OwnCloud (stylized ownCloud) is a suite of client-server software for creating file hosting services and using them. OwnCloud is functionally very similar to the widely used Dropbox, with the primary functional difference being that OwnCloud is free and open-source, and thereby allowing anyone to install and operate it without charge on a private server, with no limits on storage space (except for disk capacity or account quota) or the number of connected clients.

Installing MariaDB

ownCloud requires a database server, so we’ll start by installing MariaDB.
The following command will install MariaDB server and client:

$ sudo apt-get install mariadb-server

Once you have MariaDB installed you need to secure your installation. This will guide you through some steps.

$ sudo /usr/bin/mysql_secure_installation

Creating the ownCloud user and database

Now you need to login to the MySQL command line with the root credentials.
Use the following command, and type your root password.

$ mysql -u root -p

Once logged in we create a new database for ownCloud:

MariaDB [(none)]> create database owncloud;

and a new user:

MariaDB [(none)]> grant usage on *.* to owncloud@localhost identified by 'somepassword';

and give that user access to the newly created database:

MariaDB [(none)]> grant all privileges on owncloud.* to owncloud@localhost;

Now you have a user owncloud with password somepassword which has access to the database called owncloud.

Installing PHP-FPM

Since PHP 7 was released a couple of months ago, there is no reason not to install this newer version.

First we need to add the repository which contains PHP 7:

$ sudo add-apt-repository ppa:ondrej/php
$ sudo apt-get update

Now we can install PHP:

$ sudo apt-get install php7.0-fpm

Simply installing PHP isn’t enough, in order to have ownCloud working properly, you also need some extra PHP extensions. A list of all ownCloud requirements can be found here. The extensions below are recommended for ownCloud and not included in the default PHP installation:

  • PHP mysql driver
  • PHP gd
  • PHP curl
  • PHP intl
  • PHP mcrypt
  • PHP imagick

You can install them all at once:

$ sudo apt-get install php7.0-mysql php7.0-gd php7.0-curl php7.0-intl php7.0-mcrypt 

If you need previews for videos and documents you also need to install the following (non-PHP) packages:

  • ffmpeg or avconv
  • LibreOffice

For optimal integration with Caddy, accepting PHP-FPM request on a TCP socket instead of a Unix socket is better:
Replace listen = /run/php/php7.0-fpm.soc to listen = 127.0.0.1:9000 in /etc/php/7.0/fpm/pool.d/www.conf.

Don’t forget to restart PHP-FPM: sudo service php7.0-fpm restart after installing all extensions.

Caddyfile

I made the following Caddyfile together with mholt and dprandzioch. The config contains everything you need for hosting ownCloud server and also supports the desktop and mobile clients.

my-owncloud-site.com {

    root owncloud
    log owncloud/access.log
    errors owncloud/access.log

    fastcgi / 127.0.0.1:9000 php {
            env PATH /bin
    }

    rewrite {
        r ^/index.php/.*$
        to /index.php?{query}
    }

    # client support (e.g. os x calendar / contacts)
    redir /.well-known/carddav /remote.php/carddav 301
    redir /.well-known/caldav /remote.php/caldav 301

    # remove trailing / as it causes errors with php-fpm
    rewrite {
        r ^/remote.php/(webdav|caldav|carddav)(\/?)$
        to /remote.php/{1}
    }

    rewrite {
        r ^/remote.php/(webdav|caldav|carddav)/(.+)(\/?)$
        to /remote.php/{1}/{2}
    }

    status 403 /forbidden

    # .htacces / data / config / ... shouldn't be accessible from outside
    rewrite {
        r ^/(?:\.htaccess|data|config|db_structure\.xml|README)
        to /forbidden
    }
    
    header / Strict-Transport-Security "15768000"
    
}

Thanks to Caddy’s Let’s Encrypt integration, our ownCloud installation is automatically secured and served over HTTPS. Access and error logs are written to the ownCloud folder, and the data folder (together with other special files) is protected against requests from the outside.

If you want to test your Caddyfile / PHP installation, you can create a phpinfo.php file in the owncloud directory, and put the following line into it:

<?php phpinfo(); ?>

Then navigate to https://my-owncloud-site.com/phpinfo.php with your browser and check if the default PHP info page is displayed. (of course after Caddy is running with your Caddyfile)
Don’t forget to delete this file after everything is working! Other people don’t need to know your exact PHP installation with limits and extensions.

Installing & Configuring ownCloud

Now it’s finally time to install ownCloud.

Download the latest ownCloud version (at the time of writing 9.0.0 was the latest version, check this for yourself):

$ wget https://download.owncloud.org/community/owncloud-9.0.0.zip

Unzip the files into the owncloud directory:

$ unzip owncloud-9.0.0.zip

Go to https://my-owncloud-site.com with your web browser. If everything works fine, you will see the ownCloud configuration screen.

However, you need to create a data folder for ownCloud and give ownCloud (i.e. www-data user) write/read access to it.

$ mkdir owncloud/data
$ sudo chown -R www-data owncloud

Concluding

Caddy’s rapid development makes it a choice candidate for serving your OwnCloud installation securely and easily.

This blogpost was originally written for the official Caddy blog: Running ownCloud with Caddy

Getting a Mac ready for development

Apple computers are awesome: they are fancy machines which make your daily life easier. But every developer knows that there are a lot of things to install and configure before you can actually start programming. This guide is an overview of how I configure my Mac for development. I’m sure everyone has his own habits regarding tools and configurations, but this guide should get you started!

Note: I wrote this guide while configuring my Mac running El Capitan (OS X 10.11). Things might (and probably will) change for future OS X versions.

Command line tools

Fish shell

Fish is “a smart and user-friendly command line shell for OS X, Linux, and the rest of the family.” It makes your life in the terminal a lot easier: it comes with autosuggestions, man page completion, nice colours, web-based configuration, and a clean and simple scripting language.

Download Fish from their website and run the installer. After the installation you must manually set the shell as your default shell:

$ chsh -s /usr/local/bin/fish

Fish functions

You can easily create your own function with Fish. To do so, you must create a file with the function name in the ~/.config/fish/functions directory.

Fish prompt

The prompt is a Fish function itself, which you can fully customize. Here is the prompt I use for Fish (~/.config/fish/functions/fish_prompt.fish):

function fish_prompt --description 'Write out the prompt'

    set -l last_status $status
    
    if not set -q __fish_prompt_normal
        set -g __fish_prompt_normal (set_color normal)
    end
    
    # PWD
    set_color $fish_color_cwd
    echo -n (prompt_pwd)
    set_color normal
    
    printf '%s ' (__fish_git_prompt)
    
    if not test $last_status -eq 0
        set_color $fish_color_error
    end
    
    echo -n '$ '

end
Opening files with Chocolat editor

I have a Fish function which opens a given folder or file with my favorite editor (Chocolat, see below). Put this function in ~/.config/fish/functions/chocolat.fish:

function chocolat
    if test -z "$argv"
        open . -a Chocolat
    else
        open $argv -a Chocolat
    end 
end
sudo !!

Fish has no sudo !! command. If you really need it, you can create your own (~/.config/fish/functions/sudo.fish):

function sudo
    if test "$argv" = !!
        eval command sudo $history[1]
    else
        command sudo $argv
    end
end

Xcode

First you need to install Xcode from the Mac App Store. Once installed, you also need the Command Line Tools, you can initiate the download process by executing $ xcode-select --install in the Terminal.

Vim

Vim is my favorite editor in the console. No need to install it because it’s part of the command line tools. You can completely customize it: I have the following Vim config in ~/.vimrc:

" Vim config

set number                      "Line numbers are good
set backspace=indent,eol,start  "Allow backspace in insert mode
set showcmd                     "Show incomplete cmds down the bottom
set showmode                    "Show current mode down the bottom
set gcr=a:blinkon0              "Disable cursor blink
set visualbell                  "No sounds

" enable syntax highlighting
syntax on   

" indentation
set autoindent
set smartindent
set smarttab
set shiftwidth=4
set softtabstop=4
set tabstop=4
set expandtab

MacPorts

MacPorts is a package manager for OS X, which is quite similar to FreeBSD Ports. Yeah, I know, some people prefer Homebrew
Download the MacPorts installer from their website (make sure to download it for the correct operating system) and install MacPorts.

If you are using Fish (MacPorts does it automatically if you use Bash, which is the default one on OS X), you must manually set your PATH correctly. Add the following line to ~/.config/fish/config.fish:

$ set -xg PATH /opt/local/bin /opt/local/sbin $PATH

After installing MacPorts it’s good practice to update MacPorts and the ports tree:

$ sudo port selfupdate

Git

Git comes together with the Xcode command line tools.
Make sure to correctly setup your Git config, since your username and email will appear in every commit you author.

$ git config --global user.name "Some username"
$ git config --global user.email some_mail@example.com

Go

Since I’m a Go developer, installing Go is one of the first things I do on a new Mac. Installing Go can be done very easily using MacPorts:

$ sudo port install go

You must also define your Go path. More info about how the $GOPATH should look like can be found here.
Using Fish shell, you must add it to ~/.config/fish/config.fish:

set -xg GOPATH ~/Code/Go/

Using Bash:

$ export GOPATH=~/Code/Go/

To make this permanent (for Bash) you must put the command in your .bash_profile.

Optionally you also add your Go bin folder to your $PATH. For Fish, you must again put it in ~/.config/fish/config.fish:

$ set -xg PATH $GOPATH/bin $PATH

C++

Xcode comes with Clang compiler by default. If you need Gcc, you must install it using MacPorts:

$ sudo port install gcc5

Now you need to enable the MacPorts Gcc version, so you actually use it when executing the gcc command. (Apple linked the Gcc command to Clang…):

$ port select --list gcc
Available versions for gcc:
    mp-gcc5
    none (active)

$ sudo port select --set gcc mp-gcc5

You can verify your installation in the Terminal: $ gcc -v

Python

Python 2.7 comes bundled with the OS X command line tools. If you need a newer version of Python, you can install it using MacPorts:

$ sudo port install python35

You can also install pip, an awesome tool for installing extra Python packages/modules:

$ sudo easy_install pip

MAMP (Mac OS X Apache MariaDB PHP)

Doing web development? Then you probably want to install MAMP. See this blogpost for detailed instructions.

You know Caddy? Probably not, but it’s surely worth checking out! It’s a very easy and simple webserver which works perfectly with PHP-FPM.

Other ports

Install some other useful ports you might need:

$ sudo port install cmake wget pandoc mtr sl boost qt5 nodejs

Composer

Install Composer, the PHP package manager:

$ curl -sS https://getcomposer.org/installer | php
$ sudo mv composer.phar /usr/local/bin/composer

Sass

Install the Sass tool for preprocessing CSS stylesheets:

$ sudo gem install sass

LaTeX

You can download and install a complete LaTeX bundle called MacTex. It comes with a .pkg installer and doesn’t require any additional configuration.

Software

This is a list of software I use (or just know). The tools listed here are worth checking out and are surely not new for most Mac users.

Editors

There are a lot of good code editors available for Mac. My personal favorite is Chocolat. Here is an overview of some good editors for OS X:

General programming:

Web development:

IDEs:

LaTeX editor: Texpad (paid)

Utilities

Some other useful apps for developers.
FTP:

Although I think you shouldn’t use a visual Git client and stick to the command line, there are some good Git clients for OS X:

Others:

Raspberry Pi with HifiBerry running Volumio, an awesome music streamer!

I guess everyone knows the Raspberry Pi, a tiny, extendable single-board computer that is capable of doing almost everything. But have you already used the Raspberry Pi with the HifiBerry, a better sound card? The HifiBerry is a high fidelity DAC (digital-to-analog converter) which brings high-quality sound to the Pi. (Yes, it’s very easy to install, it’s just a smaller board you put on the Raspberry Pi)

Having hardware for a hifi streaming device is the first part, but software is even more important… Ever heard of Volumio? It’s a complete operating systems with a built-in music player that you can run on a lot of devices (including the Raspberry Pi). Installing it on my Pi with HifiBerry, it allowed me to stream from any device to my hifi DENON amplifier and a lot more.

Setup

Here is our living room setup:

  • Rasberry Pi B+
  • HifiBerry DAC+
  • Volumio
  • Good old DENON amplifier with excellent speakers
Raspberry Pi with HifiBerry DAC next to Denon amp.

Raspberry Pi with HifiBerry DAC next to Denon amp.

Volumio

Volumio is an incredible open source project, you can install it very easily (it’s well documented on their website) on your Raspberry Pi. Once installed, you can navigate to the IP (or Bonjour .local address) of the Pi and access the control panel where you can adjust any settings you want. (It’s where you can easily select the HifiBerry Dac for output, enable Airplay streaming, or install updates …). The web inteface comes with a very nice music player:

Volumio web interface on iPad

Volumio web interface on iPad

Streaming

Streaming to the Pi from iPad/iPhone/Mac is incredibly easy (using Apple’s Airplay protocol of course). On iOS you click on the “Airplay” icon in the control center, and then you select your Volumio.

On OS X you click on the sound icon while holding the alt (= option) key, and you select your Volumio. You can also click on the “Airplay” icon in iTunes if you only want to stream your music. (which is what I usually do).

On Android things are bit harder. If you are lucky (which I was by buying a Meizu m2 with some preinstalled apps), your phone comes with a default music app, which supports DLNA out the box. This makes things (almost) as easy as on iOS.

If however, you don’t have such an app, you need to look for solutions. You can stream al your sound using AirAudio (root required) or stream your music using doubleTwist or BubbleUPnP. There are surely other apps available for streaming to DLNA/Airplay. I’m fairly new to Android, so feel free to comment with good music players for Android which support Volumio!

Streaming from Linux (Ubuntu in this example) is also possible, using DLNA, you can do so using Pulseaudio:

$ sudo add-apt repository ppa:qos/pulseaudio-dlna
$ sudo apt-get update && sudo apt-get install pulseaudio-dlna
$ pulseaudio-dlna

You can then go to “Settings”, “Volume” and select your Volumio.

Raspberry Pi Volumio HifiBerry USB drive

USB

You can also play music from a USB drive. Simply attach your USB storage to the Pi, and refresh the data folder in Volumio. This way you can play any music you or your friends put on the drive. Note that this probably won’t work with portable hard drives which require power from the Pi. A powered USB hub (or just using a powered USB drive instead) solves this.

Webradios

Volumio comes standard with a huge list of webradio streams, which is one of the features I really like. You can easily select a webradio, stream it for a whole day. That way you can discover new music, since Volumio reads the metadata from the webradio and displays some info.

But there is more: you aren’t limited to this list of web radios. You can add web radio’s by downloading the .m3u file and uploading it to your Raspberry Pi, by using the Volumio shares you can find in the list of network devices.

Others

And there are even more ways to listen to your favorite content. You can also upload files to your Raspberry Pi (again using the Volumio shares on your computer) and play them directly, without having to put them on a USB drive.

You can also play music from your NAS, using Samba or NFS.

Conclusion

All together this is the perfect streaming solution for me. For less than 90 euros, I have a complete setup, which is easy to use: everyone in the house understands the system. And when I show it to other they are immediately interested! A family member was so keen that he already bought himself the complete kit (Pi + Volumio + HifiBerry) and installed it for himself!

I was also astound by the sound quality of the HifiBerry DAC+: I expected decent quality, but the sound quality in combination with the DENON amp is really excellent. If you already have an external DAC (which I have not in the living room), you can also buy the HifiBerry Digi. This gives you a bit-perfect digital output signal to your own DAC, which makes it possible to omit all possible noise from the Raspberry Pi’s power supply (which is not that great).

Note: The developers of Volumio are currently working Volumio 2, which will even be cooler!

An introduction to PHP 7 type declarations

PHP 7 introduced a feature everyone was waiting for: typing. This solves a lot of bugs/problems caused by wrong types passed to function or returned by functions.

Strict typing vs type coercion

Before I introduce the actual typing of arguments or return values, I must explain PHP’s behaviour when types don’t match.

By default PHP tries to convert the given variable to the defined type. This is called coercion or type juggling:

By default, PHP will coerce values of the wrong type into the expected scalar type if possible. For example, a function that is given an integer for a parameter that expects a string will get a variable of type string.

The example below shows what can go wrong with that (this is also why you need to use the === operator when comparing things):

$foo = "0";  // $foo is string (ASCII 48)
$foo += 2;   // $foo is now an integer (2)
$foo = $foo + 1.3;  // $foo is now a float (3.3)
$foo = 5 + "10 Little Piggies"; // $foo is integer (15)
$foo = 5 + "10 Small Pigs";     // $foo is integer (15)

So if we want actually PHP to throw an exception when types don’t match, we must manually enable strict typing (in each file which calls the function):

It is possible to enable strict mode on a per-file basis. In strict mode, only a variable of exact type of the type declaration will be accepted, or a TypeError will be thrown. The only exception to this rule is that an integer may be given to a function expecting a float.

Strict typing applies to function calls made from within the file with strict typing enabled, not to the functions declared within that file. If a file without strict typing enabled makes a call to a function that was defined in a file with strict typing, the caller’s preference (weak typing) will be respected, and the value will be coerced.

This done by adding the following line to the file:

declare(strict_types=1);

It yields the following TypeError exception when types don’t match:

Fatal error: Uncaught TypeError: Argument 1 passed to factorial() must be of the type integer, string given, called in - on line 9 and defined in -:4
Stack trace:
#0 -(9): factorial("5")
#1 {main}
  thrown in - on line 4

You can catch these TypeError exceptions like any other exception:

try {
    factorial("5");
} catch (TypeError $e) {
    echo 'Error: '.$e->getMessage();
}

Types of function arguments

The first sort of typing I will demonstrate is the type declaration of function arguments. Since PHP 7 it’s possible to give parameters of a function a type, the type of the arguments is then checked on function call, and if the callee defined strict typing, a type error is thrown when they don’t match:

function factorial(int $n) {
    return $n * ($n-1);
}

If we check this with both an int and a string:

declare(strict_types=1);

echo factorial(5);

echo factorial("5");

We got the following result:

20

PHP Fatal error:  Uncaught TypeError: Argument 1 passed to factorial() must be of the type integer, string given, called in /Users/Mathias/Sites/types.php on line 15 and defined in /Users/Mathias/Sites/types.php:5
Stack trace:
#0 /Users/Mathias/Sites/types.php(15): factorial('5')
#1 {main}
  thrown in /Users/Mathias/Sites/types.php on line 5

If we don’t specify strict typing, we got two times 20 as result.

This can naturally also be used to check type conformance when passing instances of user-defined classes to functions or implementations of interfaces:

class Fruit {}
class Apple extends Fruit {}

class Car {}


function getTaste(Fruit $f) {
    // return something useful :)
}
declare(strict_types=1);

getTaste(new Fruit);
getTaste(new Apple);
getTaste(new Car);

As one would expect, both Fruit and Apple are accepted, but Car is not accepted. Note that a subtype always conforms to the supertype, even PHP properly follows the Liskov substitution principle1. The above code yields the following results:

PHP Fatal error:  Uncaught TypeError: Argument 1 passed to getTaste() must be an instance of Fruit, instance of Car given, called in /Users/Mathias/Sites/types.php on line 19 and defined in /Users/Mathias/Sites/types.php:12
Stack trace:
#0 /Users/Mathias/Sites/types.php(19): getTaste(Object(Car))
#1 {main}
  thrown in /Users/Mathias/Sites/types.php on line 12

Types of return values

The return type declarations are similar to the argument type declarations:

function factorial($n): int {
    return $n * ($n-1);
}

This can again also be done with class instances or interface implementations.

If we now call the factorial function with a float, it will return a float instead of the integer we specified in the function declaration:

declare(strict_types=1);

echo factorial(5.5);

This yields:

PHP Fatal error:  Uncaught TypeError: Return value of factorial() must be of the type integer, float returned in /Users/Mathias/Sites/types.php:13
Stack trace:
#0 /Users/Mathias/Sites/types.php(20): factorial(5.5)
#1 {main}
  thrown in /Users/Mathias/Sites/types.php on line 13

When should I use type declarations?

Always? The more you use type declarations, the less time you will have to spend debugging your code. The most useful advantage of type checking is that you don’t need to manually check for Null values being passed to functions or returned by functions. PHP type declarations encode the pre- and postconditions you have to implement manually otherwise. So the more typed functions you have, the quicker you will discover problems, and the faster you can develop!

Especially when developing with others, you want strong preconditions, so the callee is more restricted in what he may pass to your function. Having stronger postcondition restricts what you can return, and makes it easier for the callee.

Notes

  • Typing is (as expected) not as advanced as in other languages like C++/Java/Go, and doesn’t make PHP a statically typed language.
  • When overriding class methods of a parent, the child should use the same type.

    When overriding a parent method, the child’s method must match any return type declaration on the parent. If the parent doesn’t define a return type, then the child method may do so.

    Any good programmer should ignore the last sentence in the quote: preconditions (being the type declarations in this case) can be modified in redefined routines, but they may only be weakened.2 So PHP actually doesn’t fully conforms to Liskov substitution principle.

  • When you’re working with strict typing, you sometimes want to coerce the types yourself. You can do this using type casting:
    $foo = 1;   // $foo is an integer
    $bar = (boolean) $foo;   // $bar is a boolean
    $fst = (string) $foo; // $fst is a string

  1. Liskov substitution principle: if S is a subtype of T, then objects of type T may be replaced with objects of type S. Feel free to read the original paper by Barbara Liskov: Family Values: A Behavioral Notion of Subtyping
  2. Meyer, Bertrand, Object-Oriented Software Construction, second edition, Prentice Hall, 1997, p. 570-573

Migrating/updating from PHP 5 to PHP 7 on OS X (MacPorts)

PHP 7 logoPHP 7 was released earlier this month, time for me to upgrade my development environment to keep up to date with this new version. I had already installed PHP 5.6 using MacPorts, so this guide is about upgrading a PHP 5 installation to a PHP 7. If you need help for actually installing PHP/Apache/MySQL, read this blogpost: Installing MAMP (Mac OS X Apache MariaDB PHP) using MacPorts.

Install PHP 7

Install PHP 7, and the extensions you need:

$ sudo port install php70
$ sudo port install php70-cgi php70-gd php70-curl php70-intl php70-iconv php70-gettext php70-mbstring php70-imap php70-mcrypt php70-xmlrpc php70-mysql php70-openssl php70-sockets php70-zip php70-tidy php70-opcache php70-xsl php70-sqlite

Install the Apache handler for PHP 7:

$ sudo port install php70-apache2handler

Activating PHP 7

Once installed you must activate the handler:

$ cd /opt/local/apache2/modules
$ sudo /opt/local/apache2/bin/apxs -a -e -n php7 mod_php70.so

This may lead to two PHP modules being loaded in the Apache configuration file. If phpinfo() still uses PHP 5.6 (or any other PHP version), you need to uncomment the PHP 5 module in /opt/local/apache2/conf/httpd.conf.

#LoadModule php5_module        modules/mod_php56.so
LoadModule php7_module        modules/mod_php70.so

At the bottom of the same file, you need to add this line for PHP 7 (and possibly delete the same line for PHP 5):

Include conf/extra/mod_php70.conf

Now we have configured PHP 7 and Apache, we need to restart Apache:

$ sudo port unload apache2; \
  sudo port load apache2

or

$ sudo /opt/local/etc/LaunchDaemons/org.macports.apache2/apache2.wrapper restart

Select PHP 7 as the current PHP version:

$ sudo port select php php70

You can verify this by executing $ sudo port select --list php:

Available versions for php:
    none
    php55
    php56
    php70 (active)

Verifying the PHP version

Verify the executable using PHP cli:

$ php -v
PHP 7.0.1 (cli) (built: Dec 29 2015 19:22:02) ( NTS )
Copyright (c) 1997-2015 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2015 Zend Technologies

Now create a test PHP file on your webserver and verify in the browser if Apache uses the correct PHP version:

<?php phpinfo(); ?>

Running Caddy Server as a service with systemd

In a previous blogpost I explained how to run Caddy (a brilliant and simple web server) as a service using Upstart. Systemd, however, replaced Upstart on most of the operating systems, so it makes more sense to have a guide for systemd.

Caddy executable

We don’t run Caddy as root to keep things as secure as possible. To allow a non-root user to listen on port 80 or 443, we must allow the executable to do so:

$ sudo setcap cap_net_bind_service=+ep ./caddy

systemd unit file

Now we create a systemd configuration file /etc/systemd/system/caddy.service, which looks like this on my server:

[Unit]
Description=Caddy webserver
Documentation=https://caddyserver.com/
After=network.target

[Service]
User=mathias
#Group=some_group
WorkingDirectory=/home/mathias
LimitNOFILE=4096
PIDFile=/var/run/caddy/caddy.pid
ExecStart=/home/mathias/caddy -agree -email my_lets_encrypt@mail.com -pidfile=/var/run/caddy/caddy.pid
Restart=on-failure
StartLimitInterval=600

[Install]
WantedBy=multi-user.target

A brief explanation of the directives:

  • After=network.target will automatically start Caddy after all networking stuff is loaded at boot.
  • User, Group and WorkingDirectory speak for themselves… Make sure that your Caddyfile is in the working directory. You can always manually specify the location of the Caddyfile: $ caddy -conf="/path/to/Caddyfile".
  • LimitNOFILE=4096 sets max file descriptor (to a good production value), like you would do with ulimit -n 4096 in your terminal.
  • ExecStart is the actual script that will be executed. In this case it runs the Caddy binary which is in my home directory, agrees the Let’s Encrypt user agreement, and sets a default email for your Let’s Encrypt account. If you don’t add those two flags, Caddy will prompt for user input, which will naturally not work when you start it as a daemon.
  • Restart=on-failure will automatically respawn the Caddy process if Caddy crashes (i.e. stopping with non-zero exit code).
  • StartLimitInterval=600 makes sure that if Caddy crashes too many times (being 5 by systemd’s default value) it will wait 10 min before trying again.
  • WantedBy specifies a target we want this service to be included with.

Make sure too take a peak at the systemd documentation, since it’s very powerful  and supports a lot of features!

Running the service

After you have created the service file, you must enable it: sudo systemctl enable caddy. Don’t forget to reload the service file if you make changes to it: systemctl daemon-reload.

Now you can start Caddy using sudo service caddy start. And of course stop, restart, …

You can view the status of your service using sudo service caddy status.

Swift: “Open file…” dialog with NSOpenPanel

For a simple Swift Mac app I needed to be able to give the user a modal to browse files and then display it in a textfield. Luckily Cocoa has the NSOpenPanel which does exactly this. In this blogpost I give an example of how to use NSOpenPanel.

Example app

For this example I have a text field and a push button. A click on the button will load the dialog and afterwards the path of file the user selects will appear in that field.

NSOpenPanel example app

 

In my example the field has the name filename_field in the ViewController class. If you don’t know how to create outlets for interface elements, read my earlier blogpost.

“Open file…” dialog

To open the dialog we create the following function (in the ViewController):

@IBAction func browseFile(sender: AnyObject) {
    
    let dialog = NSOpenPanel();
    
    dialog.title                   = "Choose a .txt file";
    dialog.showsResizeIndicator    = true;
    dialog.showsHiddenFiles        = false;
    dialog.canChooseDirectories    = true;
    dialog.canCreateDirectories    = true;
    dialog.allowsMultipleSelection = false;
    dialog.allowedFileTypes        = ["txt"];

    if (dialog.runModal() == NSModalResponseOK) {
        let result = dialog.URL // Pathname of the file
        
        if (result != nil) {
            let path = result!.path!
            filename_field.stringValue = path
        }
    } else {
        // User clicked on "Cancel"
        return
    }
    
}

First we create a new instance of the NSOpenPanel, on which we set some properties (like which file type we allow to be chosen or wether the user can select directories or multiple files). Then we open the actual dialog: dialog.runModal(). We check for the return value of runModal() so we can ignore it if the user cancelled the modal, this also works for other modals like NSSavePanel.
We extract the URL from the model once it is closed and store it in the file_name text field.

Toggling the dialog

The last thing we need to do is open the dialog once the user clicks on the “Select file” button. To do so we click on the little circle at the left of the function declaration and drag the line to the button.

NSOpenPanel button click example Xcode

Now we have a working OS X app with a browse file dialog. Build & run it, everything should work!

Syntax highlighting in LaTeX with lstlisting

When adding code snippets to my LaTeX documents, I use lstlisting. It is a syntax highlighting environment that can easily be tweaked to match your own styling.

LaTex listlisting syntax highlighting example

Example of syntax highlighting with lstlisting and the style defined below.

To do this you need to use the package listings\usepackage{listings}.

Code snippets are added like this:

\begin{lstlisting}
Some super cool code snippet...
\end{lstlisting}

Adding a code snippet from file is also possible:

\lstinputlisting{source_filename.py}

You can changes the styling of the code fragments (making keywords bold, comments italic, changing colors, …) by specifying lstset:

\lstset{ %
  backgroundcolor=\color{white},   % choose the background color; you must add \usepackage{color} or \usepackage{xcolor}
  basicstyle=\footnotesize,        % the size of the fonts that are used for the code
  breakatwhitespace=false,         % sets if automatic breaks should only happen at whitespace
  breaklines=true,                 % sets automatic line breaking
  captionpos=b,                    % sets the caption-position to bottom
  commentstyle=\color{commentsColor}\textit,    % comment style
  deletekeywords={...},            % if you want to delete keywords from the given language
  escapeinside={\%*}{*)},          % if you want to add LaTeX within your code
  extendedchars=true,              % lets you use non-ASCII characters; for 8-bits encodings only, does not work with UTF-8
  frame=tb,	                   	   % adds a frame around the code
  keepspaces=true,                 % keeps spaces in text, useful for keeping indentation of code (possibly needs columns=flexible)
  keywordstyle=\color{keywordsColor}\bfseries,       % keyword style
  language=Python,                 % the language of the code (can be overrided per snippet)
  otherkeywords={*,...},           % if you want to add more keywords to the set
  numbers=left,                    % where to put the line-numbers; possible values are (none, left, right)
  numbersep=5pt,                   % how far the line-numbers are from the code
  numberstyle=\tiny\color{commentsColor}, % the style that is used for the line-numbers
  rulecolor=\color{black},         % if not set, the frame-color may be changed on line-breaks within not-black text (e.g. comments (green here))
  showspaces=false,                % show spaces everywhere adding particular underscores; it overrides 'showstringspaces'
  showstringspaces=false,          % underline spaces within strings only
  showtabs=false,                  % show tabs within strings adding particular underscores
  stepnumber=1,                    % the step between two line-numbers. If it's 1, each line will be numbered
  stringstyle=\color{stringColor}, % string literal style
  tabsize=2,	                   % sets default tabsize to 2 spaces
  title=\lstname,                  % show the filename of files included with \lstinputlisting; also try caption instead of title
  columns=fixed                    % Using fixed column width (for e.g. nice alignment)
}

When you use your own colors, you must naturally define them:

\usepackage{xcolor}

\definecolor{commentsColor}{rgb}{0.497495, 0.497587, 0.497464}
\definecolor{keywordsColor}{rgb}{0.000000, 0.000000, 0.635294}
\definecolor{stringColor}{rgb}{0.558215, 0.000000, 0.135316}

If needed you can also specify your own language syntax:

\lstdefinelanguage{JavaScript}{
  keywords={typeof, new, true, false, catch, function, return, null, catch, switch, var, if, in, while, do, else, case, break},
  keywordstyle=\color{blue}\bfseries,
  ndkeywords={class, export, boolean, throw, implements, import, this},
  ndkeywordstyle=\color{darkgray}\bfseries,
  identifierstyle=\color{black},
  sensitive=false,
  comment=[l]{//},
  morecomment=[s]{/*}{*/},
  commentstyle=\color{purple}\ttfamily,
  stringstyle=\color{red}\ttfamily,
  morestring=[b]',
  morestring=[b]"
}

More info can be found in the LaTeX documentation: Source Code Listings.