Hide Slack unread and highlight Tray Icon on Ubuntu/Linux

The Slack Ubuntu/Linux application doesn’t provide a way to disable the tray icon and that means the unread or highlight notification bubbles will distract you whenever somebody writes something in ANY channel that you are in, even when the messages are not even interesting for you.

I wrote to Slack support to make a feature request and they suggested to make clever use of an upstream Electron bug that makes the tray icon disappear in 17.04 as a workaround. This gave me the idea for a more stable workaround.

I poked around in the list of files of the slack-desktop package and found out that you can just exchange the files for the tray icons , so that the Slack app is fooled into thinking it is showing the unread or highlight icons.

sudo -i
cd /usr/lib/slack/resources/app.asar.unpacked/src/static
mv slack-taskbar-highlight.png slack-taskbar-highlight-backup.png
mv slack-taskbar-unread.png slack-taskbar-unread-backup.png
cp slack-taskbar-rest.png slack-taskbar-highlight.png
cp slack-taskbar-rest.png slack-taskbar-unread.png

Since I probably need to re-run this after every update to the Slack app, I packed this into a shell script slack-quiet.

Why does the PHP extension fail to load with _zval_ptr_dtor_wrapper error

I sometimes come across this error when loading an extension in PHP

PHP Warning:  PHP Startup: Unable to load dynamic library
'/usr/lib64/php-zts/5.5/modules/myext.so' -
/usr/lib64/php-zts/5.5/modules/myext.so: undefined symbol:
_zval_ptr_dtor_wrapper in Unknown on line 0

From the Google results it is not very easy to find out what this means.

Technically the extension was compiled with a PHP version using the -enable-debug flag, which wraps all calls to zval_ptr_dtor in a wrapper macro that does not exist in a regular, non-debug build.

But a debug extension and non-debug PHP are incompatible.

PHP extensions are only compatible between different installations if the following constraints are the same for the compiling and the executing PHP binary:

  • OS (Linux, Mac)
  • libc (glibc or musl for Alpine)
  • PHP version up to the minor level (5.5, 5.6, 7.0, 7.1)
  • extension version (20151012 for PHP 7.0)
  • thread-safety (ZTS) or non-thread-safety (NTS)
  • debug or non-debug (--enable-debug flag during compile).
More about: PHPExtension

How to complete a side project and turn it into a business (Level 1)

Almost four years ago I wrote about Lessons learned: A failed side project and when I stumbled across the post How to *never* complete anything several days ago I felt it was time for an update. Ewan’s post covers similar lessons learned that I wrote about in my post and I heard about many side projects that failed because of similar mistakes.

There was an argument on HackerNews what a side project is, so I want to clarify this up front. I am talking about side-projects written with some intend of turning them into a business, as opposed to side-projects that are started for learning new technical skills.

Three years ago I have started working on a PHP profiling and monitoring side-project, which I turned into a business called Tideways. In this post I wanted to share some of the reasons why I think this project was successful.

The idea for “XHProf as a Service” was on my “Side Project Ideas” trello board, where I write everything down that I want to try or experiment with. I have picked it out for implementation as my new side project, because that month I would have needed this for a large scale load-testing project at my job with Qafoo. It was not the first time I needed this for myself, I felt a regular pain that a product like this didn’t exist.

Not wanting to make the same mistakes, I have applied all the lessons learned from 2013:

  • I picked a small scope: The first version contained very few features, re-used the existing xhprof PHP extension (Tideways now has its own) and instead of a daemon collecting data in realtime used a cronjob to collect data from a directory every minute. After six months of development I removed half of the features I experimented with to reduce the scope again.
  • I did not compete with the established competition on features, instead I focussed on two single features that I thought where the most important: Response time monitoring and Callgraph profiling. By focussing on a niche (PHP intead of all languages) I was able to provide a better solution than existing generic tools (obviously biased).
  • I did not work all alone on the project, instead I have immediately enlisted alpha-users that gave valuable feedback after 1 month of work and some of them are still our most active customers; brought in my employer Qafoo who is now a shareholder in the new business; formed business partnerships with companies that are now our biggest customers and re-sellers.
  • I did not keep the idea secret, when I announced our beta program publically in June 2014 the launch newsletter list received 250 sign ups and 60 Twitter followers in a few hours.
  • I choose boring technology, using a monolithic Symfony, PHP, MySQL backend and jQuery frontend stack allowed me to iterate very fast on new features with technology that I already mastered. I spent three innovation tokens on using Golang for the daemon, using Elasticsearch as TimeSeries database and later on learning C for the PHP extension.

Making these decisions has not magically turned my side project into a profit-generating business. However I have avoided all the problems that I have written about in my failed side project lessons post from four years ago.

The fast iterations on a small scope combined with early user feedback showed that this idea will work as a business and it was worthwhile to keep pushing for two years, until the project generated enough revenue after all other costs to pay my full time salary.

This felt like passing level 1 in getting a bootstrapped side project of the ground (there are many more levels after that).

More about: SideProjects / Bootstrapping

Explicit Global State with Context Objects

Global State is considered bad for maintainability of software. Side effects on global state can cause a very nasty class of bugs. Context objects are one flavour of global state. For example, I remember that Symfony1 had a particularly nasty context object that was a global singleton containing references to very many services of the framework.

As with every concept in programming, there are no absolute truths though and there are many use-cases where context objects make sense. This blog posts tries to explain my reasons for using context objects.

What is a Context?

Context is defined as all the information and circumstances in which something can be fully understood. In daily programming this is mostly related to the state of variables and databases. Some examples in the world of PHP include the superglobals $_GET and $_POST.

Any piece of code is always running in some context and the question is how much of it is explicit in the code and how much is hidden.

A context object is a way to make the existing context explicit for your code.

Context Objects

Lets take a look at the Definition of a context object

A context object encapsulates the references/pointers to services and configuration information used/needed by other objects. It allows the objects living within a context to see the outside world. Objects living in a different context see a different view of the outside world.

Besides the obvious use of encapsulating services and config variables, this definition mentions two important properties:

  1. Allows to see the outside world, which does not mean it can change it. In my opinion it is essential that context objects are immutable, to avoid side effects.
  2. The possibility of objects living in different contexts, seeing different context objects suggets that a context object should never be a singleton.

By using objects instead of global variables for context, we can use encapsulation to achieve immutability.

Context already exists in PHP through the existance of superglobals. Frameworks usually wrap them to achieve the properties mentioned above: Immutability and Not-Singleton.

The Symfony Request object is one good example, where these properties (almost) hold. Wrapping the superglobals with this object even allowed creating Subrequests inside PHP requests.

Application Context

Now that we have defined context we should make use of context objects in your applications.

Anything that is an important global information in your application is relevant for the context:

  1. Building a wiki? I actually have the idea for this approach from Fitnesse, a testing tool based on the wiki idea maintained by Uncle Bob and his team. Their context object provides access to the current and the root page nodes.
  2. Building a shop? The users basket id, selected language/locale, current campaign (newsletter, google, social media?) can be information that should be available all the time.
  3. Building an analytics software? The currently selected date/time-range is probably important for all queries.
  4. Building a CMS/blog? The current page/post-id, the root page id, user language/locale seem to be good candidates for an application context. Wordpress does this although their context is global and encapsulated in an object.
  5. Building a multi-tenant app? The tenant-id and configuration for this tentant (selected product plan, activated features, ...) are good candidates for the context.

Real World Example: Context in Tideways

How to introduce such a context? We could create an object in our application, for example how we did it in Tideways with selected tenant (organization), application, date-range and server environment:

<?php

class PageContext
{
    /**
     * @var \Xhprof\Bundle\OrganizationBundle\Entity\Organization
     */
    private $organization;

    /**
     * @var \Xhprof\Bundle\OrganizationBundle\Entity\Application
     */
    private $application;

    /**
     * @var \Xhprof\Common\Date\DateRange
     */
    private $selectedDateRange;

    /**
     * @var \Xhprof\Bundle\ProfilerBundle\View\EnvironmentView
     */
    private $selectedEnvironment;

    // constructor and getters
}

This object is created during request boot, in my case with a framework listener. The listener checks for access rights and security constraints, showing the 403/access denied page when necessary. This make 90% of access control checks unneeded that are usually cluttering the controller.

The context is then made available for the application by using a Symfony parameter converter, every controller-action can get access to the context by type-hinting for it:

<?php

class ApplicationController
{
    public function showAction(PageContext $pageContext)
    {
        return array('application' => $pageContext->getApplication());
    }
}

The beauty of this approach is avoiding global state and passing the context around in a non-singleton way. Depending on the framework you use, it might be hard to achieve this kind of context injection.

Now when I build lightweight Symfony2 controllers in my applications, using a context object allows me to use even less services and move repetitive find and access control code outside of the controllers.

I have also written a Twig extension that gives me access to the context object, so I don’t have to return it from every controller and created a wrapper for the URL Generation that appends context information to every URL (current date range + environment):

<h1>{{ pageContext.application.name }}</h1>

<a href="{{ page_path("some_route") }}">Link with Context query arguments</a>

Conclusion

A context object can help you make global state explicit and control access to it. Good requirements for a context object are immutability and not being a singleton.

When used correctly this pattern can save you alot of redundant code and simplify both controllers and views massively.

The pattern has its drawbacks: You have to be careful not put too powerful objects into the context and if you can modify the context, then you will probably introduce nasty side effets at some point. Additionally if you don’t make sure that creating the context is a very fast operation then you will suffer from performance hits, because the context is created on every request, maybe fetching expensive data that isn’t even used.

Composer Monorepo Plugin (previously called Fiddler)

I have written about monorepos in this blog before, presented a talk about this topic and released a standalone tool called “Fiddler” that helps integrating Composer with a monolithic repository.

At the beginning of the year, somebody in #composer-dev IRC channel on Freenode pointed me in the direction of Composer plugins to use with Fiddler and it was an easy change to do so.

With the help of a new Composer v1.1 feature to add custom commands from a plugin, Fiddler is now “gone” and I renamed the repository to the practical beberlei/composer-monorepo-plugin package name on Github. After you install this plugin, you have the possibility to maintain subpackages and their dependencies in a single repository.

$ composer require beberlei/composer-monorepo-plugin

To use the plugin add monorepo.json files into each directory of a subpackage and use a format similar to the composer.json to add dependencies to a.) external composer packages that you have listed in your global Composer file b.) other subpackages in the current monorepo. See this example for a demonstration:

{
    "deps": [
        "vendor/symfony/http-foundation",
        "components/Foo"
    ],
    "autoload": {
        "psr-0": {"Bar": "src/"}
    }
}

This subpackage here defined in a hypothetical file components/Bar/monorepo.json has dependencies to Symfony HTTP foundation and another subpackage Foo with its own components/Foo/monnorepo.json. Notice how we don’t need to specify versions (they are implicit) and import other dependencies using the relative path from the global composer.json.

The monorepo plugin is integrated with Composer, so every time you perform install, update or dump-autoload commands, the subpackages will be updated as well and each get their own autoloader that can be included from vendor/autoload.php relative to the subpackages root directory as usual.

More about: Monorepos / Composer