PHP Composer plugin for packaging

Hi all,

There are already some methods of packaging PHP projects, but I don’t believe anyone as taken the approach of writing a Composer plugin yet. So I decided to give it a shot:

Using a plugin has the advantage of automatically keeping the generated Nix expression up-to-date. Plus it works even for developers without Nix!

Hope you’ll find it useful. :slight_smile:

5 Likes

Awesome! I look forward to trying this out.

Hi @stephank ,

Do you have an example of Symfony application using your plugin?

I made one that works, but I would like to compare with something from you.

I don’t have one yet, because I have not deployed anything using the full Symfony framework. I’ve only used individual components.

I did a small experiment, starting from symfony new --webapp, and it looks it should work with some small adjustments.

  • You probably need to overwrite getCacheDir() and getLogDir() in src/Kernel.php, because those are hardcoded to use the project directory, which will not be writable. Maybe allow an environment variable override, so it works in development, but can be overridden in deployment:
public function getCacheDir(): string
{
    return $_ENV['APP_CACHE_DIR'] ?? parent::getCacheDir();
}

public function getLogDir(): string
{
    return $_ENV['APP_LOG_DIR'] ?? parent::getCacheDir();
}
  • Encore is based on Node.js, so needs to be combined with a different Nix packaging solution. I didn’t look into this for now. I myself built yarn-plugin-nixify for Node.js packaging, but there’s also node2nix, yarn2nix, etc. Once you’ve done your Encore build using one of these, you can probably just copy over public/build/ in your PHP derivation.

Other than that, serve result/libexec/myapp/public/ with PHP-FPM. When using the NixOS module, you can add additional systemd settings on top to create cache/log directories and set environment variables. Rough example, I didn’t test this:

users.groups.myapp = { };
users.users.myapp = {
  group = "myapp";
  isSystemUser = true;
};

services.phpfpm.pools.myapp = {
  phpPackage = pkgs.php81;  # Maybe add extensions here.
  user = "myapp";
  group = "myapp";
  settings = {
    "listen.group" = "nginx";
    "pm" = "static";
    "pm.max_children" = 2;
    "pm.max_requests" = 500;
  };
};
systemd.services.phpfpm-myapp = {
  serviceConfig = {
    CacheDirectory = "myapp";
    LogsDirectory = "myapp";
  };
  environment = {
    APP_CACHE_DIR = "/var/cache/myapp";
    APP_LOG_DIR = "/var/log/myapp";
  };
};

services.nginx.virtualHosts."myapp.test" = {
  locations."/" = {
    root = "${myapp-package}/libexec/myapp/public";
    tryFiles = "$uri $uri/ /index.php$is_args$args";
  };
  locations."~ \\.php$".extraConfig = ''
    fastcgi_pass unix:${config.services.phpfpm.pools.myapp.socket};
    include ${config.services.nginx.package}/conf/fastcgi_params;
  '';
};

Hope that answers some of your questions?

Yes and no.

I’m not on my computer right now, I cannot be verbose at the moment.

But what I’m looking is a flake file that would run a symfony app very easily with nix, but not necessarily on Nixos.

If you just want to run a command and see the webapp running, I assume you’re trying to do non-production stuff? (I don’t know how to make the Nginx / PHP-FPM stuff portable to, say, Nix on other Linux distros or nix-darwin.)

What you could do is symfony server:start (in the project dir) or php -S localhost:8000 index.php (in the public dir). You can use postInstall to write an extra shell script to $out/bin/ containing that command. Then you can do something like nix-build && ./result/bin/serve-myapp.

Since you mentioned flakes, there’s also the defaultApp attribute which should allow you to do just nix run, I believe. But haven’t played with that yet myself. See the wiki, I guess. :upside_down_face:

You still need a working build and figure out where Symfony can store cache and logs, though. Maybe you just do export APP_CACHE_DIR="$PWD/var/cache" in your bin script if you expect the user will always run it from the project directory. That’d mean it reuses the normal directories from the project.

What I’m trying to achieve to have a basic flake.nix ready to run any PHP app by just doing nix run and without having a composer.lock file in it.

The nix run command would run the default PHP server on public/ (php -S 0.0.0.0:8000 -t public/)

I could found a proper way to do that yet.

This is what I have so far: flake.nix for PHP apps · GitHub