Learn and Discover Open Source with Daily Genuine Experience. From Coding, Design, Package Management Tweak to Desktop Customization.


Goal: Separate Main Flow, Code, and Data.

So anyone can focus to alter special customization in Main Script, without changing the whole stuff.

Which PHP

Since there many PHP version. I should say that I use PHP7. But don’t worry, it is portable. I do not use any sophisticated code.


Before you jump off to scripting, you might desire to read this overview.

All The Source Code:

Impatient coder like me, like to open many tab on browser.

Modularized HerbstluftWM in Many Languages

This article is one part of a collection. All integrated, one related to another. So we can compare each other quickly.

Tutorial/ Guidance/ Article: [ Modularized Overview ] [ BASH ] [ Perl ] [ Python ] [ Ruby ] [ PHP ] [ Lua ] [ Haskell ]

Source Code Directory: [ BASH ] [ Perl ] [ Python ] [ Ruby ] [ PHP ] [ Lua ] [ Haskell ]

Directory Structure

Directory Structure has been explained in preface. This figure will explain how it looks in PHP script directory.

HerbstluftWM: Directory Structure

Modularizing in PHP

Nothing to say here. PHP is simple

Declare a module

No need to explicitly define what to export.

Nothing to write in helper Script

Call a module


System Calls

Here we wrap herbstclient system call in a function named hc.


function hc($arguments) 
    system("herbstclient $arguments");


# Read the manual in $ man herbstluftwm
hc('emit_hook reload');

# gap counter
system("echo 35 > /tmp/herbstluftwm-gap");

Array: Tag Names and Keys


$tag_names = range(1, 9);
$tag_keys  = array_merge(range(1, 9), [0]);

HerbstluftWM: Tag Status

Hash: Color Schemes

Using key-value pairs, a simple data structure.


$color = array(
    'white' => '#ffffff',
    'black' => '#000000',

    'grey50'  => '#fafafa',
    'grey100' => '#f5f5f5',


# background before wallpaper
system("xsetroot -solid '".COLOR['blue500']."'");

View Source File:

Hash: Config

The Hash in Config is very similar with the colors above. Except that it has string interpolation all over the place.


# Modifier variables
$s = 'Shift';
$c = 'Control';
$m = 'Mod4';
$a = 'Mod1';

$keybinds = array(
  # session
    "$m-$s-q" => 'quit',
    "$m-$s-r" => 'reload',
    "$m-$s-c" => 'close'

This config will be utilized in main script as shown in the following code.


do_config("keybind",   $keybinds);
do_config("keybind",   $tagskeybinds);
do_config("mousebind", $mousebinds);
do_config("attr",      $attributes);
do_config("set",       $sets);
do_config("rule",      $rules);

View Source File:

Processing The Hash Config

This is the heart of this script.

This do-config function has two arguments, the herbstclient command i.e “keybind”, and hash from config. I can see how simple and clean, PHP is.


function do_config($command, $hash) 
    # loop over hash
    foreach ($hash as $key => $value) {
        hc($command.' '.$key.' '.$value);

        # uncomment to debug in terminal
        # echo $command.' '.$key.' '.$value."\n";

Debug Herbstclient Command

I do not remove line where I do debug when I made this script, so anyone can use it later, avoid examining blindly. Sometimes strange things happen. Just uncomment this line to see what happened.

        echo $command.' '.$key.' '.$value."\n";

You can see the debugging result in figure below.

HerbstluftWM: Debug Command

View Source File:

Setting the Tags

Nothing special here, PHP read all exported variable from modules, but PHP function do not read the varable unless it defined global.


function set_tags_with_name()
    global $tag_names, $tag_keys;
    hc("rename default '$tag_names[0]' 2>/dev/null || true");
    foreach($tag_names as $index=>$value) {
        hc("add '$value'");
        $key = $tag_keys[$index];
        if (!empty($key)) {
            hc("keybind Mod4-$key use_index '$index'");
            hc("keybind Mod4-Shift-$key move_index '$index'");

Launch the Panel

Two more functions left, it is do_panel and startup_run.

This two also be easy to do in PHP. helper.php

function do_panel() 
    $panel   = __dir__."/panel-lemonbar.php";
    if (!is_executable($panel))
        $panel = "/etc/xdg/herbstluftwm/panel.sh";

    $raw = shell_exec('herbstclient list_monitors | cut -d: -f1');
    $monitors = explode("\n", trim($raw));

    foreach ($monitors as $monitor) {
        # start it on each monitor
        system("$panel $monitor > /dev/null &"); // no $output

Run Baby Run

This is the last part. It is intended to be modified. Everyone has their own personal preferences.


function startup_run() 
    $command = 'silent new_attr bool my_not_first_autostart';
    system("herbstclient $command", $exitcode);

    # without redirection your app WILL HANG !
    if ( ! ((bool) trim($exitcode)) ) {
      # non windowed app
        system("compton > /dev/null &");
        system("dunst > /dev/null &");
        system("parcellite > /dev/null &");
        system("nitrogen --restore > /dev/null &");
        system("mpd > /dev/null &");

      # windowed app
        exec("xfce4-terminal > /dev/null 2>&1 &");
        exec("firefox > /dev/null 2>&1 &");
        exec("geany > /dev/null 2>&1 &");
        exec("thunar > /dev/null 2>&1 &");

Specific PHP Issue

Without redirection, your script will hang.

Be aware of the > /dev/null & part in PHP. It is mandatory for running background processing. The PHP manual has said it clearly.

View Source File:

Putting It All Together.

The last part is going to main script and putting it all back together.

Now the flow is clear

Header Part: autostart.php


Procedural Part: autostart.php

# background before wallpaper
system("xsetroot -solid '".COLOR['blue500']."'");

# Read the manual in $ man herbstluftwm
hc('emit_hook reload');

# gap counter
system("echo 35 > /tmp/herbstluftwm-gap");

# do not repaint until unlock

# standard
hc('keyunbind --all');
hc("mouseunbind --all");
hc("unrule -F");


# do hash config
do_config("keybind",   $keybinds);
do_config("keybind",   $tagskeybinds);
do_config("mousebind", $mousebinds);
do_config("attr",      $attributes);
do_config("set",       $sets);
do_config("rule",      $rules);

# unlock, just to be sure

# launch statusbar panel

# load on startup

View Source File:

Coming up Next

After the Window Manager, comes the Panel.

Happy Configuring.