Vai al contenuto

Do the right thing – Fa la cosa giusta

Questo piccolo articolo nasce dall’esperienza personale lavorativa che mi permette di lavorare su molteplici tipologie di linguaggio e sulle più disperate applicazioni, da quelle monolitiche a quelle altamente scalabili, ma in tutte o quasi, noto che il programmatore o i programmatori che si sono cimentati nell’impresa prima di me, non si sono soffermati sul rendere il codice:

  • testabile
  • leggibile
  • con documentazione

Facciamo un po’ di chiarezza

Il codice è testabile quando vengono scritte tutte le funzioni lato test, quindi si tratta in definitiva, che per ogni metodo scritto, ne deve esistere uno identico utilizzabile solo per test. Tra i sistemi più noti per effettuare i test, esiste lo unit testing:

In ingegneria del software, per unit testing (testing d’unità o testing unitario) si intende l’attività di testing (prova, collaudo) di singole unità software. Per unità si intende normalmente il minimo componente di un programma dotato di funzionamento autonomo; a seconda del paradigma di programmazione o linguaggio di programmazione, questo può corrispondere per esempio a una singola funzione nella programmazione procedurale, o una singola classe o un singolo metodo nella programmazione a oggetti.

fonte: wikipedia

Il codice è leggibile quando qualsiasi persona in grado di comprendere il codice sorgente, riesce facilmente ad interpretare le operazioni e la logica utilizzata per raggiungere un dato scopo

Leggibile: Che si riesce a leggere, che è cioè facilmente decifrabile, chiaro alla lettura

fonte: treccani

Codice documentato o con documentazione è quel codice che non necessariamente deve possedere un libro di testo scritto, ma bensì, che per ogni metodo o funzione scritta, ci sia brevemente una descrizione di cosa si sta eseguendo. Per fare questo, esistono i commenti.

Il commento, nell’ambito dei linguaggi di programmazione, è una parte del codice sorgente che ha il solo scopo di descriverne le caratteristiche funzionali, ovvero di spiegare il funzionamento delle successive linee di codice, e che non fa parte dell’algoritmo risolutivo codificato in linguaggio di programmazione.

fonte wikipedia

Esempi pratici

Sappiate sin da subito che esiste una libreria in dotazione a qualsiasi linguaggio per scrivere uno unit/test. Poniamo l’esempio con PHP (non a caso è quello che ha applicativi dove al 99% dei casi, nessuno lo fà).
In PHP esiste la libreria PHPUnit, facilmente installabile tramite composer. Senza riportare codice di esempio o scriverne uno nuovo, sul sito ufficiale, esistono diversi esempi a partire dal primo.

Scrivere codice leggibile è una cosa veramente facile, basta solamente avere un briciolo di buon senso logico

<?php

(int)$a = 4;
if( $a == 0 || $a == 1 || $a == 2 || $a == 3 )
{
    (bool) $result = true;
} else {
    (bool) $result = false;
}

// meglio se faccio in questo modo:
// definisco una mappa di numeri accettati
(array) $map = [
    0,1,2,3
];

(int) $a = 4;
// utilizzo una funzione nativa di PHP 
// per controllare se esiste o meno quel dato
// nel mio array
if( in_array($a, $map) )
{
    (bool) $result = true;
} else {
    (bool) $result = false;
}

// posso scriverla ancora più leggibile? Certo
(array) $map = [
    0,1,2,3
];

(int) $a = 4;

(bool) $result = false;
if( in_array($a, $map) )
{
    $result = true;
}

// extra sulla leggibilità, 
// trasformiamo tutto in funzione e separiamo la logica
function test( int $number, array $map = [] ) : bool
{
    if( in_array($number, $map) ) {
        return true;
    }
    return false;
}

(array) $map = [
    0,1,2,3
];

(int) $a = 4;
(bool) $result = test( $a );

Come è possibile notare, man mano che ho riscritto il medesimo codice, risulta sempre più leggibile e comprensibile anche al primo sguardo. Ma la vera chicca, arriva quando il codice è commentato.

Utilizzando la parte finale del codice sopra indicato, è possibile segnalare all’utente lettore del codice, cosa stiamo facendo e cosa esegue la funzione test()

<?php

/**
 * Controlla se il dato numerico esiste nella mappa/array
 * 
 * @param int $number
 * @param array $map
 * @return bool
 */
function test( int $number, array $map = [] ) : bool
{
    if( in_array($number, $map) ) {
        return true;
    }
    return false;
}

So che ad un primo sguardo la sola cosa che si pensa è: “Cavolo ma c’è da scrivere ancora”, ebbene si anche se, tuttavia la maggior parte degli IDE, hanno in pancia un sistema per scrivere le annotazioni in maniera del tutto automatizzata (la descrizione va scritta a mano e preferibilmente in inglese)

Visto come è tutto molto più semplice e pulito in questo modo?

Piè di pagina

Mi farebbe piacere conoscere la vostra esperienza e soprattutto che metodologia utilizzate, se la utilizzate, per standardizzare quanto più possibile il vostro codice e soprattutto, se sto dicendo delle cavolate, perchè magari sono il solo a cercare di vederla in questo modo.

Come creare una coppia di chiavi RSA con openSSL

Mini sheet con le informazioni utili per generare una coppia di chiavi RSA

RSA cheat sheet

# Genera una chiave privata con la lunghezza corretta
openssl genrsa -out private-key.pem 3072

# Genera la corrispondente chiave pubblica
openssl rsa -in private-key.pem -pubout -out public-key.pem

# opzionale: create un certificato self-signed
openssl req -new -x509 -key private-key.pem -out cert.pem -days 360

# optionale: converte il .pem in .pfx
openssl pkcs12 -export -inkey private-key.pem -in cert.pem -out cert.pfx

React.lazy, importare dinamicamente i componenti con e senza default export

A partire dalla release v16.6 di React è possibile splittare il codice in diversi file che verranno caricati dinamicamente a seconda della navigazione, rendendo la nostra applicazione più leggera.

Lo scenario più comune di utilizzo è quando il nostro modulo o componente è esportato come default, quindi la conversione è la seguente

// Caricamento standard di un componente
import Component from './component'

const Home = () => {
    return <div>
            <Component />
        </div>
}
// con React.lazy rendiamo dinamico il caricamento
const Component = React.lazy(() => import("./component"));

const Home = () => {
    return <div>
            <Component />
        </div>
}

Ora, che succede se il nostro modulo non è esportato come default?

Secondo la regolare sintassi, il modulo o componente, lo potremmo importare in questo modo:

//component.jsx
export const Component = <h1>Sono il tuo componente</h1>

// home.jsx
import {Component} from './component'

const Home = () => {
    return <div>
            <Component />
        </div>
}

React.lazy, sfrutta il dynamic import che ritorna una Promise che deve restituire sempre un modulo esportato come default.

Allora a noi ci basta semplicemente risolvere tale Promise e riassegnare il nuovo valore come default.

//component.jsx
export const Component = <h1>Sono il tuo componente</h1>

// home.jsx
const Component = React.lazy(() => import("./component").then( module => ({default: module.Component})));

const Home = () => {
    return <div>
            <Component />
        </div>
}

Piccola nota a margine.

Ricordati sempre che i moduli che sono importati dinamicamente, devono utilizzare React.Suspense per essere correttamente importati:

const Home = () => {
    return <div>
            <React.Suspense fallback={<>Sto caricando...</>}>
                <Component />
            </React.Suspense>
        </div>
}

Come fare la build di un’immagine Docker di un’applicazione NextJs

Abbiamo bisogno di mettere in produzione la nostra applicazione NextJs e ci serve un’immagine Docker leggera su dove farla girare?

Creiamo il nostro Dockerfile in questo modo

# Se stai facendo copia ed incolla, abbi cura di realizzare un .dockerignore!

# Iniziamo il deploy come dipendenza
FROM node:16.13.0-alpine AS deps

# Se ti serve libc per le tue dipendenze, togli il commento dalla riga sottostante:
# RUN apk add --no-cache libc6-compat

# Copiamo solamente il package.json ed installiamo i moduli!
WORKDIR /app
COPY package.json ./
RUN npm install --frozen-lockfile

# END DEPS IMAGE

# Ora creiamo un container solamente per la Build
FROM node:16.13.0-alpine AS BUILD_IMAGE

# Setting della nostra work directory
WORKDIR /app

# Copiamo dalla nostra DEPS, aggiungiamo la'pplicazione
# ed eseguiamo la nostra build di produzione
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

# Rimuoviamo tutte le dipendenze
# e rieseguiamo l'install ma questa volta con il flag --production.
RUN rm -rf node_modules
RUN npm install --production --frozen-lockfile --ignore-scripts --prefer-offline

# END OF BUILD_IMAGE

# Adesso startiamo la nostra immagine finale.
FROM node:16.13.0-alpine

ENV NODE_ENV production

RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001

# Andiamo a prenderci i file necessari dalla BUILD_IMAGE - e ci servirà:
# 1. il package.json
# 2. la Next build e tutti gli static files
# 3. i node_modules.
WORKDIR /app
COPY --from=BUILD_IMAGE --chown=nextjs:nodejs /app/package.json ./
COPY --from=BUILD_IMAGE --chown=nextjs:nodejs /app/node_modules ./node_modules
COPY --from=BUILD_IMAGE --chown=nextjs:nodejs /app/public ./public
COPY --from=BUILD_IMAGE --chown=nextjs:nodejs /app/.next ./.next

# 4. Opzionale se la tua applicazione ha un next.config.js
COPY --from=BUILD_IMAGE --chown=nextjs:nodejs /app/next.config.js  ./

USER nextjs

EXPOSE 3000

CMD [ "npm", "start" ]

Dump o Restore da un MySql Docker container

Spesso capita di dover eseguire il dump di dati che sono salvati in un container MySql, altrettanto spesso capita di dover eseguire un’importazione massiva.. come fare?

Da riga di comando, è possibile fare tutto

#!/bin/bash
## mysqldump
docker exec -i CONTAINER_NAME /usr/bin/mysqldump -u DB_USER -p'DB_PASS' DB_NAME > dump.sql

## Restore
cat dump.sql | docker exec -i CONTAINER_NAME /usr/bin/mysql -u DB_USER -p'DB_PASS' DB_NAME

Installare Docker Engine su Ubuntu

Primo articolo della categoria Read Less, do More

Ci occupiamo senza troppi fronzoli di installare il Docker engine su una di queste versioni di Ubuntu:

  • 18.04
  • 20.04
  • 21.10
  • 22.04

Disinstalla le vecchie versioni di Docker se installate:

sudo apt-get remove docker docker-engine docker.io containerd runc

Installa le dipendenze

sudo apt-get update

sudo apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

Aggiungi la GPG key ufficiali di Docker

sudo mkdir -p /etc/apt/keyrings

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

Esegui il set up della repository

echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Installa Docker engine ed i suoi componenti

sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin

Testiamo la nostra installazione

sudo docker run hello-world

Post install (non è necessario, ma andrebbe eseguito)

Aggiungi il tuo utente al gruppo Docker (così eviterai di usarlo tramite sudo)

sudo usermod -aG docker $USER

Esegui il logout ed LogIn. Adesso riprova ad eseguire docker senza il comando sudo

docker run hello-wold

Keycloak, deploy in locale di un sistema di Single Sign-On potente e versatile

Da Wikipedia:

Keycloak è un prodotto software open source per consentire il single sign-on con Identity and Access Management rivolto ad applicazioni e servizi moderni.

Da un punto di vista pratico è un software che permette di gestire i criteri di autenticazione di applicazioni o servizi attraverso i protocolli SAMLv2 o OpenID connect /OAuth2 in perfetta sicurezza.

Scenario d’uso

Immaginiamoci di avere un network di applicazioni ognuna con i propri clienti. In questo modo i diversi applicativi del network, non solo non possono condividere informazioni dell’utente, ma ogni applicazione potrebbe seguire una propria logica di autenticazione.

In uno scenario in cui vorremmo che l’utente fosse federato al nostro network e di conseguenza l’autenticazione fosse centralizzata, avendo criteri di accesso diversificati per applicazioni, come faremmo?

Sappiate che, oltre ad essere un bel rompicapo è anche piuttosto difficile da realizzare mantenendo degli standard di sicurezza elevati.

Gli altri cosa fanno in merito?

Poniamo l’esempio più lampante: Google!

Google ha in pancia una miriade di applicazioni a cui possiamo farne accesso semplicemente avendo un account in Google Account.

Quindi perchè anche noi, non possiamo fare grosso modo la stessa medesima cosa? Ed ecco che Keycloak ci viene in aiuto

Nota

Keycloak è uno dei tanti IM (Identity Manager) esistenti, lo stesso Google (Google IAM) o Microsoft (Azure AD B2C) ne possiedono, ma sono solamente a pagamento perchè gestiti dai rispettivi fornitori.

In realtà Keycloak essendo un prodotto di RedHat ne esiste una versione gestita in toto da RedHat stesso ed anche questa a pagamento.

Dato che noi vogliamo innanzitutto prendere confidenza con questo applicativo al solo scopo didattico, non ci interessa pagare nessuno, ma capirne l’importanza mettendo in piedi un servizio Keycloak localmente così da poter sperimentare quanto detto sopra.

Mettiamo in piedi l’applicativo

Requisiti:

  • Docker
  • docker-compose

Aprite la vostra shell e digitate quanto segue:

# a partire della nostra $HOME creiamo una nuova directory e accediamo in essa
mkdir ~/keycloak-test
cd ~/keycloak-test

# scarichiamo il docker-compose di esempio
curl -s https://gist.githubusercontent.com/stefanopascazi/f6ca929194cbf57f228d78ca03b94d54/raw/cc2cfbb66f049c0bab3917e774f5de273e6ab46f/keycloak-docker-compose.yml -o docker-compose.yml

# lanciamo i nostri container
docker-compose up --build

Al termine della procedura di installazione, potremmo visionare il nostro Keycloak appena deployato visitando la url: http://localhost:8481

Clicchiamo su Administration Console ed accediamo con le credenzilai:

  • admin
  • password

E benvenuto nel vostro Master Realm

Step by Step: creiamo un reame, un utente di test, un client e tentiamo un autorizzazione

Step 1: Creiamo un nuovo Reame in Keycloak

Cerchiamo di spiegare cosa sono i Realms o reami (in Italiano).

Keycloak si divide in Realms (altri applicativi chiamano la medesima funzione Tenants) ovvero un’applicazione interna che può includere diversi client che condividono gli stessi utenti.
Perchè non lavorare sul Master? Beh molto semplice, il realm master è quello con privilegi amministrativi globali.

Quindi, dalla barra a sinistra, posizioniamoci sulla voce Master e clicchiamo sul pulsante “add realm“, diamo un nome al nostro realm (realmtest) e clicchiamo create

Otterrete una schermata come questa

Step 2: creiamo un utente

Dal nostro realm di test nella barra di sinistra clicchiamo su users e nella schermata successiva add user.

Riempiamo i campi richiesti come segue e clicchiamo save:

Si ricaricherà la pagina, mostrando delle nuove tab aggiuntive.

Clicchiamo sulla tab Credientials e compiliamo i campi come in immagine e clicchiamo set password

Step 3: realizziamo un client per la nostra applicazione

Dalla barra di sinistra, clicchiamo su clients e nella nuova schermata clicchiamo create

Assegniamo un ID al nostro client (myclient) e clicchiamo su save

Compiliamo come in schermata l’access-type impostandolo su confidential e la valid redirect url, successivamente clicchiamo save

A questo punto abbiamo finito.

Preleviamo il client_secret dalla tab Credentials, facendo molta attenzione che Client Authenticator sia impostato su Client ID and secret e copiamo il secret

Step 4: eseguiamo la nostra prima autenticazione

Apriamo un terminale ed eseguiamo la nostra cURL in questo modo modificando il client_secret con il vostro appena prelevato:

curl --location --request POST 'http://localhost:8481/realms/realmtest/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'client_id=myclient' \
--data-urlencode 'client_secret=xjxdex2ATUGcSOtq7aTOLQqsT3rI7p72' \
--data-urlencode 'username=test' \
--data-urlencode 'password=Test12345!'

Se avete eseguito tutto come descritto, otterrete una risposta di questo tipo:

{
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJBVjlXUGJfWG9IWWFGYmpIVzhkYTFkcUdjX1Q0ckpWams5TFpuNmpaeEk4In0.eyJleHAiOjE2NTU4OTM5OTksImlhdCI6MTY1NTg5MzY5OSwianRpIjoiZDM3Zjg1NzMtZjE0Mi00OTk5LWFiYzQtNDZmZGI5MzkxMmYwIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4NDgxL3JlYWxtcy9yZWFsbXRlc3QiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiMGQ5ZDEwNzItNjJjOC00NmQ5LTk3ZjEtZTU2Y2ZiMjI1NjVkIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoibXljbGllbnQiLCJzZXNzaW9uX3N0YXRlIjoiN2Y3N2M0NzktMzAzZS00OTFkLWI1NTItY2FjYTVlNjRhNGZmIiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsImRlZmF1bHQtcm9sZXMtcmVhbG10ZXN0IiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6InByb2ZpbGUgZW1haWwiLCJzaWQiOiI3Zjc3YzQ3OS0zMDNlLTQ5MWQtYjU1Mi1jYWNhNWU2NGE0ZmYiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwicHJlZmVycmVkX3VzZXJuYW1lIjoidGVzdCIsImVtYWlsIjoidGVzdEBlbWFpbC5pdCJ9.BNm7-Waji0Ad7XC3Hxp_mrb8kuXJ5TB7QYKkwT1AgAh2F9mEptPUwK6ntH6DPL7fghd2fbXUVWzGOdEGkgWT2aAw6m6oU786gZYc8VKKiEIf9B94FY1FrGIDVGhlAlTcaG7WuXghvnvLSrZR5gYiz4BUbMT3oqyHUB7lcryGscoVTZ6wzoqZyo-YbovokEpzvBezxVLMJbjrMCbOBpHTP4RQ56k_6sNmiJZxJCPLeOMH7XCwll5Q6uwBoTRbx_Ihx4XLUEaEz7M8eAqckaO6h6hw9YkL061FVOiTG8MNH6WBzYP9xulMTQgo8o0sWCwtd3zM9D5bAj5RGsf-UJYJ_Q",
    "expires_in": 300,
    "refresh_expires_in": 1800,
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJmMmI3NjMwOS1kMTI0LTQ5MmYtOWFlYy0wNWNhYmIyNGMzNDAifQ.eyJleHAiOjE2NTU4OTU0OTksImlhdCI6MTY1NTg5MzY5OSwianRpIjoiMmMyZTdiNmQtM2VmNC00OWZjLWFmYWQtOTA1NDc4NzljODE4IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4NDgxL3JlYWxtcy9yZWFsbXRlc3QiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0Ojg0ODEvcmVhbG1zL3JlYWxtdGVzdCIsInN1YiI6IjBkOWQxMDcyLTYyYzgtNDZkOS05N2YxLWU1NmNmYjIyNTY1ZCIsInR5cCI6IlJlZnJlc2giLCJhenAiOiJteWNsaWVudCIsInNlc3Npb25fc3RhdGUiOiI3Zjc3YzQ3OS0zMDNlLTQ5MWQtYjU1Mi1jYWNhNWU2NGE0ZmYiLCJzY29wZSI6InByb2ZpbGUgZW1haWwiLCJzaWQiOiI3Zjc3YzQ3OS0zMDNlLTQ5MWQtYjU1Mi1jYWNhNWU2NGE0ZmYifQ.Jk4juoRQ6psuxWPL7uZB8_oefxaKKpXWtA5l0hgbI-g",
    "token_type": "Bearer",
    "not-before-policy": 0,
    "session_state": "7f77c479-303e-491d-b552-caca5e64a4ff",
    "scope": "profile email"
}

Nella prossima guida, vedremo di realizzare una piccola applicazione che si connetterà al servizio di autenticazione, ma per adesso, le basi le abbiamo realizzate.

Symfony 6.x, realizzare un’applicazione da zero

In questa mini guida realizzeremo un’applicazione da zero in Symfony 6.x .

Per prima cosa se ancora non l’abbiamo fatto è necessario installare PHP 8.1.x ed i moduli necessari che sono:

  • Ctype
  • Iconv
  • PCRE
  • session
  • SimpleXML
  • Tokenizer

Ad installazione dei moduli ultimata, assicuriamoci di avere composer installato e la Symfony CLI ( dato che ci permetterà di fare un check dei requirements del framework).

Se avete installato la Symfony CLI potete eseguire il comando:

$ symfony check:requirements

Se avete eseguito correttamente le installazioni vedrete una schermata simile a questa

Bene, siamo pronti a creare la nostra prima applicazione.

Eseguiamo il comando:

$ symfony new my_project_directory --version=6.1.*

# In caso non avessimo la Symfony CLI installata,
# possiamo usare composer in modo da ottenere il medesimo risultato:
$ composer create-project symfony/skeleton:"6.1.*" my_project_directory

A seguito dell’installazione, la nostra prima applicazione symfony è stata creata, spostiamoci nella root del progetto ed eseguiamo l’applicazione appena realizzata

$ cd my_project_directory

# Eseguiamo il web server di Symfony
$ symfony server:start --port=8080

# Se non abbiamo la Symfony CLI installata,
# possiamo eseguire il PHP server dalla cartella public del progetto
$ cd public && php -S localhost:8080

Accediamo all’indirizzo http://localhost:8080 e visualizziamo la nostra applicazione Symfony

Nota

Se è proprio il vostro primo approccio a Symfony, vi consiglio di dare uno sguardo all’applicazione di Demo che gli sviluppatori di Symfony hanno realizzato perchè è un grosso punto di partenza per uno studio iniziale di Symfony, dato che il codice è ampiamente commentato e vengono utilizzati tantissimi componenti. Installare la demo è molto semplice:

$ symfony new my_symfony_demo --demo

Extra: Creiamo il nostro primo IndexController

Se ancora non lo avete fatto, fermate il web server, aprite il progetto con il vostro IDE, posizionatevi con la vostra shell nella root principale del vostro progetto ed installiamo il componente annotation, che ci eviterà di configurare le nostre route a mano

$ composer require annotations

Ora spostiamoci all’interno della root src/Controller e creiamo il nostro primo controller:

$ touch IndexController.php

Apriamo il file appena creato con il nostro IDE ed editiamo in questo modo:

<?php

namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class IndexController
{
    /**
     * @Route("/")
     */
    public function IndexAction(): Response
    {
        return new Response(
            '<html><body><h1>Benvenuto nella tua prima pagina</h1></body></html>'
        );
    }
}

Ora, eseguiamo di nuovo il nostro server e vedremo il nostro primo Controller in azione.

Docker PHP image per Symfony 6.x application

Ho scritto e testato questa immagine, tenendomi solo lo stretto necessario, eliminando quante più cose possibili ed utilizzando una versione alpine di php8.x

Chiaramente è solo un punto di partenza per applicazioni basate su Symfony 6.x.

FROM php:8.1.5-fpm-alpine
# Install other dependencies

RUN apk add --no-cache unzip \
	libxml2-dev \
	nano \
	icu-dev \
	bash \
	util-linux \
    g++ \
    git \
    zip \
    libzip-dev

RUN apk add --no-cache  libpq-dev;

RUN apk add --no-cache  php8-dev;

RUN docker-php-ext-install \
	pdo \
	intl \
	soap \
	mysqli \
	pdo_mysql \
    opcache

RUN docker-php-ext-configure zip \
    && docker-php-ext-install zip

# set recommended PHP.ini settings
# see https://secure.php.net/manual/en/opcache.installation.php
RUN docker-php-ext-enable opcache; \
	{ \
		echo "opcache.preload=/var/www/html/config/preload.php"; \
		echo "opcache.preload_user=www-data"; \
		echo 'opcache.memory_consumption=256'; \
		echo 'opcache.max_accelerated_files=20000'; \
		echo 'opcache.validate_timestamps=0'; \
		echo 'opcache.enable=1'; \
		echo 'opcache.jit_buffer_size=100M'; \
		echo 'opcache.jit=1255'; \
		echo "realpath_cache_size=4096K"; \
		echo "realpath_cache_ttl=600"; \
	} > /usr/local/etc/php/conf.d/opcache-recommended.ini

RUN { \
    echo "upload_max_filesize=128M;"; \
	echo "post_max_size=256M;"; \
	echo "memory_limit = 512M;"; \
	echo "max_execution_time=300;"; \
	echo "session.cookie_httponly=1"; \
    echo "short_open_tag=Off"; \
} > /usr/local/etc/php/conf.d/uploads.ini


# install gd library
RUN apk add --update --no-cache freetype libpng libjpeg-turbo freetype-dev libpng-dev libjpeg-turbo-dev libwebp-dev

RUN docker-php-ext-configure gd \
    --with-freetype \
    --with-jpeg \
    --with-webp \
    NPROC=$(grep -c ^processor /proc/cpuinfo 2>/dev/null || 1)

RUN docker-php-ext-install -j$(nproc) gd \
    && apk del --no-cache freetype-dev libpng-dev libjpeg-turbo-dev

# install composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

# Install symfony-cli
RUN apk add --no-cache bash
RUN curl -1sLf 'https://dl.cloudsmith.io/public/symfony/stable/setup.alpine.sh' | bash
RUN apk add symfony-cli

# post installation
RUN cp /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini

WORKDIR /var/www/html

EXPOSE 9000

CMD ["php-fpm"]

Per buildare l’immagine eseguire il comando:

$ docker build -t symfony .

Successivamente eseguire immagine con il comando:

$ docker run --name symfony symfony