My tech blog.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Artémis 34c2b4a880 updated to php8.1, removed sentry 5 months ago
db/migrations Added a migration to fix the data model 2 years ago
public updated to php8.1, removed sentry 5 months ago
resources added proper handling of dark themes for code blocks, fixed css typo for the search button on mobile 1 year ago
src Removed portfolio page 1 year ago
.editorconfig Fixed indent style 3 years ago
.gitignore Added dotenv 2 years ago
LICENSE [WIP] First migration commit 3 years ago Updated the whole doc 2 years ago
artemis.php #54: Added auth, manual propagation shield, key gen. command 2 years ago
composer.json updated to php8.1, removed sentry 5 months ago
composer.lock updated to php8.1, removed sentry 5 months ago
config.php updated to php8.1, removed sentry 5 months ago
phinx.php patched phinx config 2 years ago
routes.php Removed portfolio page 1 year ago

My blog.


  • PHP 7.3+
    • Composer
    • PDO
    • PDO PgSQL
  • PostgreSQL 11+

Set up a local work environment

My environment is based on Apache, and is made to flawlessly work with that. I have no example configuration for anything else than Apache / PHP.


  • Apache 2.4+
    • mod_rewrite
    • mod_php7
    • mod_env: not required, but highly recommended to set once and never again environment variables.

Project setup

$ git clone
$ cd
$ composer install

Now, you need to point your DocumentRoot to

You should define the environment variables for DB_DSN and AUTH_TOKEN.

  • DB_DSN will be the PostgreSQL connection string, see the PDO docs on how to create it
  • AUTH_TOKEN will be used for authenticating the blog's owner on /admin. It can be generated by entering php artemis.php new:key and answering prompted questions.

Database setup

Once you've configured your environment, you'll have to set up the database tables.

To do that, run vendor/bin/phinx migrate, which will create the database.

You're all set!


  • db/: Migrations, as handled by phinx (may change for something simpler in the future)
  • public/: Entrypoint, static resources (e.g. icons)
    • index.php: Every request's entry point. It may be useful to read it to get a hang on what's happening on every request.
    • assets/: Every asset should be stored here
      • gpg/: The public ASC keys should be stored here
  • resources/: HTML page templates and base resources that need to be built (CSS / JS)
    • assets/: base CSS and javascript that will need to be built, if changed
    • templates/: HTML page templates, in the twig templating format
  • src/: The website's source code
    • Actions/: Every HTTP request handler, this is what's referenced in routes.php
    • Commands/: Every CLI command (which must be registered in artemis.php to be usable)
    • Helpers/: Basic PHP helper functions, to make a dev's life easier
    • Services/: OOP service classes, this contains the core backend logic
      • Renderers/: A series of classes made to provide a simpler interface to render common HTML documents
      • Repositories/: A series of classes made to provide an interface to interact with the database in a SRP-compliant way
    • Types/: Basic PHP data classes to help dealing with a few types
    • Action.php: HTTP request handler (action) interface
    • Command.php: CLI command handler interface
    • ParsedownExtended.php: Custom snippet that wraps Parsedown as dependency. Just stupidly located
    • Runner.php: Core class containing all the logic required to bootstrap the request handling system
  • artemis.php: The CLI tool, which allows to minify base resources and create keys
  • config.php: The configuration file, which contains environment-agnostic and non-secret data
  • routes.php: The configuration file, which contains every defined Siler route


The website doesn't have a concept of "middleware", but instead relies on two mechanisms to run preliminary checks (such as the authentication guard, to make sure only authorized users can access the admin panel).

  1. A "route" in siler's way is simply a code snippet that will be invoked on URI match. If a route A executes on /a, and a route B executes on /a/b, having the router set up as ['A', 'B'] will execute the actions for A, then B.
  2. This mechanism has been extended with one concept: continue / breaks. If an action's __invoke method returns the boolean value false (and nothing else), the router won't continue to try to match actions.


The website heavily relies on PHP-DI to provide access to any instance from actions. It is strongly advised to get to understand the logic dealt by the Runner::__construct and Runner::http methods before starting to dig further into the code.


You should never interact with the database directly from an Action (by injecting the Database service), but you should instead create a repository that will wrap the database access request logic into clear and simple PHP methods.