Vai al contenuto

Boson PHP. Come trasformare il tuo progetto PHP in applicazioni native multipiattaforma

Boson PHP è una piattaforma per creare applicazioni desktop multipiattaforma usando tecnologie web familiari come PHP, JavaScript, HTML e CSS. La filosofia alla base è consentire agli sviluppatori PHP di creare applicazioni native per Windows, Linux e macOS in modo semplice e performante, con un unico file eseguibile. I vantaggi principali del progetto includono:

  • Leggerezza e performance elevate.
  • Semplicità d’uso per gli sviluppatori PHP.
  • Supporto nativo per componenti web attraverso un motore WebView integrato.

Il progetto è guidato dal maintainer Kirill Nesmejanov, con contributi da Alexey Gagarin, Dmitrij Derepko e Danil Shcutskij.

I Web Components

I web components sono una caratteristica principale di Boson. In questo progetto funzionano registrando componenti personalizzati PHP che possono essere istanziati e usati come tag HTML.

Creare il primo componente

class MioPulsanteFantastico {}
$applicazione = new Boson\Application();

// Nome del tag
$nomeTag = 'mio-pulsante';

// Classe del componente
$classeComponente = MioPulsanteFantastico::class;

// Registrazione
$applicazione->webview->components->add($nomeTag, $classeComponente);

// Impostiamo il contenuto HTML
$applicazione->webview->html = '<mio-pulsante>Premimi!</mio-pulsante>';

Regola importante: il nome del tag personalizzato deve contenere un trattino.

Creare template personalizzati

È possibile personalizzare l’aspetto dei componenti.

Esempio di template HTML standard

class MioPulsanteConStile implements HasTemplateInterface
{
    public function render(): string
    {
        return <<<HTML
            <button class="btn-stiloso">Cliccami!</button>
        HTML;
    }
}

Shadow DOM per stili isolati

class MiaCarta implements HasShadowDomInterface
{
    public function render(): string
    {
        return <<<HTML
            <div class="scheda">
                <h3>Titolo della scheda</h3>
                <slot></slot> 
            </div>
        HTML;
    }
}

Con Shadow DOM gli stili e il contenuto del tuo componente restano isolati dal resto della pagina.

Gestione del ciclo di vita

Vuoi sapere quando un componente appare o scompare dalla pagina?

class MioComponenteConCiclo implements HasLifecycleCallbacksInterface
{
    public function onConnect(): void
    {
        echo "Componente connesso!";
    }

    public function onDisconnect(): void
    {
        echo "Componente rimosso!";
    }
}

Interattività: Proprietà, Metodi, Attributi ed Eventi

Proprietà sincronizzate con JavaScript

class Contatore implements HasPropertiesInterface
{
    public function onPropertyChanged(string $nomeProprieta, mixed $valore): void
    {
        if ($nomeProprieta === 'conta') {
            echo "Valore contatore aggiornato: $valore";
        }
    }

    public static function getPropertyNames(): array
    {
        return ['conta'];
    }
}

// In JavaScript
let contatore = document.createElement('mio-contatore');
contatore.conta = 10;

Metodi richiamabili da JavaScript

class Calcolatrice implements HasMethodsInterface
{
    public function onMethodCalled(string $metodo, array $argomenti = []): mixed
    {
        switch ($metodo) {
            case 'somma':
                return $argomenti[0] + $argomenti[1];
            case 'saluta':
                return "Ciao dal PHP!";
            default:
                throw new \BadMethodCallException("Metodo non trovato: $metodo");
        }
    }

    public static function getMethodNames(): array
    {
        return ['somma', 'saluta'];
    }
}

// In HTML/JS
<my-calculator onclick="this.saluta()">Calcolatrice</my-calculator>

Rilevamento attributi modificati

class BadgeStato implements HasAttributesInterface
{
    public function onAttributeChanged(string $nomeAttributo, ?string $valore, ?string $precedente): void
    {
        echo "Attributo $nomeAttributo cambiato da $precedente a $valore";
    }

    public static function getAttributeNames(): array
    {
        return ['stato'];
    }
}

Gestione eventi dal browser

class GestoreEventi implements HasEventListenersInterface
{
    public function onEventFired(string $evento, array $argomenti = []): void
    {
        if ($evento === 'click') {
            echo "Click rilevato!";
        }
    }

    public static function getEventListeners(): array
    {
        return ['click' => []];
    }
}

Contesto reattivo

Il “contesto reattivo” è un potente strumento per controllare programmaticamente lo stato del componente. In pratica PHP può modificare il contenuto HTML del componente, aggiungere o rimuovere attributi e aggiornare proprietà osservate da JS, in maniera del tutto dinamica.

ReactiveContext non è un semplice helper, ma il canale di comunicazione diretto tra PHP (logica applicativa) e DOM della WebView (HTML + JS). È un oggetto che rappresenta lo stato vivo del componente nel browser. Contiene principalmente tre aree:

  • attributes → attributi HTML del componente
  • content → contenuto HTML interno
  • (in altri contesti anche properties, events, ecc.)

Quando modifichi il ReactiveContextnon stai preparando HTML ma stai modificando direttamente il DOM già renderizzato. In pratica: ogni <componente-reattivo> nel DOM ha il suo ReactiveContextisolato dagli altri

use Boson\WebView\Api\WebComponents\ReactiveContext;

class ComponenteReattivo implements HasMethodsInterface
{
    public function __construct(private ReactiveContext $ctx) {}

    public function refresh(): void
    {
        if (!$this->ctx->attributes->has('caricato')) {
            $this->ctx->attributes->set('caricato', 'true');
        }

        if ($this->ctx->content->html === '') {
            $this->ctx->content->html = '<p>Contenuto caricato!</p>';
        }
    }

    public function onMethodCalled(string $metodo, array $argomenti = []): mixed
    {
        if ($metodo === 'refresh') {
            $this->refresh();
            return 'Aggiornato!';
        }
        return '';
    }

    public static function getMethodNames(): array //dichiara esplicitamente quali metodi sono esposti a JS
    {
        return ['refresh'];
    }
}

L’effetto reale nel DOM sarà quello di aggiungere un attributo caricato = true.

<componente-reattivo caricato="true"></componente-reattivo>

E’ importante sapere che questo effetto condiziona l’HTML esistente. Quindi non viene generata una stringa HTML, ma ribadisco, è una modifica diretta dell’elemento DOM in cui JS può reagire immediatamente a questo cambiamento.

Per quanto riguarda la funzionefunction onMethodCalled(string $metodo, array $argomenti = []): mixed viene chiamato automaticamente da Boson quando JavaScript invoca un metodo sul componente. In javascript poi potrò gestire il componente richiamandolo così:

document
  .querySelector('componente-reattivo')
  .refresh();

Cosa succede dietro le quinte?

  1. JS chiama refresh()
  2. Boson intercetta la chiamata
  3. PHP riceve "refresh" come $metodo
  4. PHP esegue refresh()
  5. PHP aggiorna DOM e attributi
  6. PHP restituisce un valore a JS

Architettura consigliata di un’applicazione Boson PHP

Una delle domande più frequenti è: come strutturare correttamente un progetto Boson PHP?

Una buona pratica è separare chiaramente:

  • Logica applicativa PHP
  • Componenti Web
  • Risorse statiche (CSS, JS)
  • Bootstrap dell’applicazione

Esempio di struttura delle cartelle

/app
  /Componenti
    PulsantePrimario.php
    BarraNavigazione.php
    FinestraDialogo.php
  /Servizi
    GestoreUtenti.php
    ArchivioDati.php
/public
  style.css
  script.js
/main.php

Questo approccio rende il progetto scalabile e facilmente manutenibile.

Bootstrap dell’applicazione

Il file main.php rappresenta il punto di ingresso dell’applicazione.

use Boson\Application;

$app = new Application();

$app->webview->html = file_get_contents(__DIR__ . '/public/index.html');

$app->run();

In questo modo puoi mantenere l’HTML separato dal codice PHP, come in una classica applicazione web.

Registrazione automatica dei Web Components

Quando il numero di componenti cresce, registrarli manualmente diventa scomodo. È possibile automatizzare il processo in questo modo:

$componenti = [
    'pulsante-primario' => App\Componenti\PulsantePrimario::class,
    'barra-navigazione' => App\Componenti\BarraNavigazione::class,
];

foreach ($componenti as $tag => $classe) {
    $app->webview->components->add($tag, $classe);
}

Questo pattern è simile a quello utilizzato nei service provider dei framework PHP moderni.

Integrazione con database e servizi

Un componente Boson non deve contenere direttamente la logica di business. È preferibile delegarla a servizi dedicati.

Esempio: servizio utenti

class GestoreUtenti
{
    public function ottieniNomeUtente(int $id): string
    {
        return "Utente #" . $id;
    }
}

Uso del servizio in un componente

class ProfiloUtente implements HasTemplateInterface
{
    public function __construct(
        private GestoreUtenti $utenti
    ) {}

    public function render(): string
    {
        $nome = $this->utenti->ottieniNomeUtente(1);

        return "<div>Profilo di $nome</div>";
    }
}

Questo approccio favorisce testabilità e separazione delle responsabilità.

Comunicazione avanzata JavaScript / PHP

Uno degli aspetti più innovativi di Boson PHP è la comunicazione bidirezionale nativa tra PHP e JavaScript. A differenza delle applicazioni web tradizionali, dove frontend e backend comunicano tramite HTTP, fetch, AJAX o WebSocket, Boson elimina completamente il livello di rete.

In Boson:

  • PHP e JavaScript convivono nello stesso processo applicativo
  • La WebView espone un bridge diretto tra JS e PHP
  • Le chiamate sono sincrone o asincrone, senza serializzazione manuale

La comunicazione avviene principalmente attraverso i Web Components PHP. Ogni componente è un elemento HTML lato JavaScript, è una classe PHP lato backend che espone metodi, proprietà, attributi ed eventi. Possiamo considerare un componente Boson come un oggetto condiviso tra JS e PHP. Il flusso tipico è il seguente:

  1. JavaScript invoca un metodo sul componente
  2. Boson intercetta la chiamata
  3. PHP riceve la chiamata e la gestisce
  4. PHP restituisce un valore (opzionale)
  5. JavaScript riceve il risultato

Tutto questo avviene senza fetch, endpoint, controller o router.

Invocare PHP da JavaScript

class Logger implements HasMethodsInterface
{
    public function onMethodCalled(string $metodo, array $argomenti = []): mixed
    {
        if ($metodo === 'log') {
            file_put_contents(
                'app.log',
                $argomenti[0] . PHP_EOL,
                FILE_APPEND
            );
        }
        return null;
    }

    public static function getMethodNames(): array
    {
        return ['log'];
    }
}
// JavaScript
document.querySelector('logger-app').log('Evento dal browser');

PHP può restituire stringhe, numeri, array e ovviamente oggetti serializzabili

case 'datiUtente':
    return [
        'id' => 1,
        'nome' => 'Mario',
        'ruolo' => 'admin'
    ];

Lato JavaScript avremo invece:

const utente = servizio.datiUtente();
console.log(utente.nome);

Gestione dello stato applicativo

Per applicazioni complesse è utile centralizzare lo stato.

Contesto globale

class StatoApplicazione
{
    private array $stato = [];

    public function set(string $chiave, mixed $valore): void
    {
        $this->stato[$chiave] = $valore;
    }

    public function get(string $chiave): mixed
    {
        return $this->stato[$chiave] ?? null;
    }
}

Questo oggetto può essere condiviso tra più componenti tramite dependency injection.

Deployment e distribuzione

Uno dei punti di forza di Boson PHP è la possibilità di distribuire un singolo eseguibile.

Il processo tipico include:

  • Compilazione dell’applicazione
  • Inclusione delle dipendenze PHP
  • Packaging per il sistema operativo target

Il risultato è un file eseguibile che non richiede PHP installato sul sistema dell’utente finale.

Quando usare Boson PHP

Boson PHP è particolarmente adatto per:

  • Applicazioni desktop leggere
  • Tool interni aziendali
  • Dashboard amministrative
  • Client multipiattaforma per API

Non è pensato per sostituire framework web tradizionali, ma per affiancarli in contesti desktop.

Conclusione

Con Boson PHP, lo sviluppo desktop diventa un’estensione naturale dell’ecosistema PHP. Grazie ai Web Components nativi, alla comunicazione diretta con JavaScript e a un modello architetturale moderno, è possibile costruire applicazioni robuste, scalabili e manutenibili.

Nei prossimi articoli potremo approfondire:

  • Testing dei componenti Boson
  • Integrazione con Laravel o Symfony
  • Gestione degli aggiornamenti automatici
  • Performance e ottimizzazione

La serie Pulse Boson PHP continua.