Configuración


1Presentación

La configuración de un proyecto que usa Temma se hace modificando el archivo temma.php ubicado en el directorio etc/, que tiene varias secciones.

Por defecto, Temma busca un archivo llamado etc/temma.php, en formato PHP (explicado más abajo). Pero puedes usar otros formatos para el archivo de configuración:

  • JSON (archivo etc/temma.json): Este formato se usa mucho en proyectos informáticos. Es el formato histórico de Temma, y sigue siendo recomendado si quieres generar tus archivos de configuración mediante un script.
  • YAML (archivo etc/temma.yaml): Este formato se usa cada vez más, especialmente en otros frameworks modernos.
  • NEON (archivo etc/temma.neon): Formato usado por el framework Nette y la herramienta de análisis estático de código PHPStan. Es muy similar al formato YAML.

Te recomendamos usar el formato PHP, ya que tiene la ventaja de almacenarse en caché mediante OPcache (la caché de OPcode, que evita tener que releer los archivos PHP cada vez que se accede a ellos), acelerando así el procesamiento. También permite la generación dinámica de la configuración.

En el directorio etc/, encontrarás cuatro archivos usados para ilustrar la configuración de un proyecto Temma:

  • temma-mini.php: Archivo mínimo que contiene las directivas básicas (formato PHP).
  • temma-mini.json: Archivo mínimo que contiene las directivas básicas (formato JSON).
  • temma-mini.yaml: Archivo mínimo que contiene las directivas básicas (formato YAML).
  • temma-full.php: Archivo que muestra todas las opciones disponibles (formato PHP).

2Archivo temma.php simple

Aquí tienes un ejemplo típico de un archivo etc/temma.php mínimo:

<?php

return [
    // configuración de la aplicación
    'application' => [
        'dataSources' => [
            'db' => 'mysql://user:passwd@localhost/mybase'
        ],
        'rootController' => 'Homepage'
    ],
    // definición de los niveles de log
    'loglevels' => 'ERROR',
    // definición de las páginas de error
    'errorPages' => 'error404.html',
    // datos importados automáticamente como variables de plantilla
    'autoimport' => [
        'googleId'          => 'azeazeaez',
        'googleAnalyticsId' => 'azeazeazeaze',
    ]
];

Podemos observar:

  • Línea 7: La definición del DSN que permite conectarse a la base de datos.
  • Línea 9: El nombre del controlador raíz, que será llamado cuando alguien se conecte sin especificar un controlador.
  • Línea 12: Definición del umbral para escribir los mensajes de log en el archivo log/temma.log. Aquí, con el umbral "ERROR", solo los mensajes con los umbrales ERROR y CRIT se escribirán realmente en el archivo de log.
  • Línea 14: Definición de la página que se mostrará si ocurre un error. La página debe estar guardada en el directorio www/ del proyecto.
  • Variables de plantilla importadas automáticamente.
    • Línea 17: ID de Google, disponible en las plantillas con {$conf.googleId} y en el controlador con $this['conf']['googleId'].
    • Línea 18: ID de Google Analytics, disponible en las plantillas con {$conf.googleAnalyticsId} y en el controlador con $this['conf']['googleAnalyticsId'].

3Archivo temma.php completo

Aquí tienes otro ejemplo, que muestra todas las variables de configuración estándar:

<?php

return [
    // Variables generales de definición de la aplicación
    'application' => [
        // fuentes de datos
        'dataSources' => [
            // DSN de conexión a la base de datos MySQL
            //  (ver el objeto \Temma\Datasources\Sql).
            // Opcional
            'db' => 'mysqli://user:passwd@localhost/mybase',

            // DSN de conexión a la base de datos Redis
            //  (ver el objeto \Temma\Datasources\Redis).
            // Opcional
            'ndb' => 'redis://localhost:6379/0',

            // DSN de conexión al servidor de caché Memcached
            //  (ver el objeto \Temma\Datasources\Memcache).
            // Opcional
            'cache' => 'memcache://localhost:11211',
        ],

        // Indica si queremos usar las sesiones o no.
        // Se usa para desactivar las sesiones cuando sea necesario.
        // Opcional: "true" por defecto.
        'enableSessions' => true,

        // Nombre de la cookie de sesión.
        // Opcional: "TemmaSession" por defecto.
        'sessionName' => 'TemmaSession',

        // Fuente de datos donde se almacenan las sesiones.
        // Opcional: Usa el mecanismo nativo de sesiones
        // de PHP por defecto
        'sessionSource' => 'ndb',

        // Duración de la sesión.
        // Opcional: Un año por defecto.
        'sessionDuration' => 31536000,

        // Indica si la cookie de sesión debe enviarse solo en
        // conexiones seguras mediante HTTPS.
        // Opcional: "false" por defecto.
        'sessionSecure' => false,

        // Nombre de dominio asociado a la cookie de sesión.
        // Opcional: no está definido por defecto.
        'cookieDomain' => null,

        // Namespace por defecto de los controladores
        // Opcional: por defecto los controladores están
        // en el namespace global.
        'defaultNamespace' => '\MyApp\Controllers',

        // Nombre del controlador usado para la raíz del sitio.
        // Opcional. Por defecto usa el controlador definido por
        // la variable 'defaultController'.
        'rootController' => 'Homepage',

        // Nombre del controlador por defecto que se usará si el
        // controlador solicitado no existe.
        // Opcional. Por defecto, genera un error 404 si se solicita
        // un controlador que no existe.
        'defaultController' => 'NotFound',

        // Nombre del controlador que se llamará siempre,
        // incluso si el solicitado existe.
        // Opcional.
        'proxyController' => 'Main',

        // Nombre de la vista por defecto.
        // Opcional. \Temma\Views\Smarty por defecto.
        'defaultView' => '\Temma\Views\Smarty',

        // Nombre del objeto que se usará como
        // componente de inyección de dependencias
        // Opcional: Usa el componente de inyección de dependencias
        // de Temma por defecto.
        'loader' => 'MyLoader',

        // Ruta del archivo de log.
        // Opcional: Por defecto, los logs se escriben en
        // el archivo "log/temma.log".
        // Pon un valor falso (null, false, cadena vacía o el
        // número cero) para desactivar la escritura en el archivo de log.
        'logFile' => 'log/temma.log',

        // Nombre del objeto de gestión de log, o lista de nombres de objetos.
        // Opcional: Por defecto no hay ningún gestor de log activado.
        'logManager' => [ 'ElasticLogManager', 'SentryLogManager' ],
    ],
    // Definición de los niveles de log
    'loglevels' => [
        'Temma/Base' => 'ERROR',
        'Temma/Web'  => 'WARN',
        'myapp'      => 'DEBUG',
        'default'    => 'NOTE',
    ],
    // Definición de los niveles de log para mensajes en buffer
    'bufferingLoglevels' => [
        'Temma/Base' => 'DEBUG',
        'myapp'      => 'DEBUG',
    ],
    // Enrutamiento: indicamos los nombres de los controladores virtuales,
    // asociándolos a un controlador real (o virtual, en cascada),
    // que se encargará de las peticiones.
    'routes' => [
        'sitemap.xml'          => 'SitemapController',
        'sitemap.extended.xml' => 'sitemap.xml',
        'robert'               => 'BobController',
    ],
    // Gestión de plugins
    'plugins' => [
        // Plugins ejecutados para todos los controladores
        // - plugins ejecutados antes del controlador
        '_pre' => [
            'CheckRequest',
            'UserGrant',
        ],
        // - plugins ejecutados después del controlador
        '_post' => [ 'AddCrossLinks' ],
        // Definición de plugins específicos del controlador Article
        'Article' => [
            // plugins ejecutados antes y después del controlador
            '_pre'  => [ 'Something' ],
            '_post' => [ 'SomethingElse' ],
            // plugins específicos de la acción index
            'index' => [
                '_pre'  => [ 'Aaa' ],
                '_post' => [ 'Bbb' ],
            ],
            // plugin ejecutado antes de la acción setData
            'setData' => [
                '_pre' => [ 'CccPlugin' ]
            ]
        ],
        // Plugin para el controlador BobController, pero
        // solo cuando se llama mediante su ruta "robert"
        'robert' => [
            '_pre' => [ 'AnotherPlugin' ]
        ],
    ],
    // Definición de las páginas de error
    'errorPages' => [
        '404'     => 'error404.html',
        '500'     => 'error500.html',
        'default' => 'error404.html'
    ],
    // Lista de rutas de inclusión para el autoloader, con o sin prefijo de namespace
    'includePaths' => [
        '/opt/some_library/lib',
        '/opt/other_lib',
        '\Acme\Log'      => '/path/to/acme-log/lib',
        '\MyLib\Feature' => '/path/to/vendors/feature',
    ],
    // Lista de rutas de inclusión de PHP
    'phpIncludePaths' => [
        '/usr/share/php',
    ],
    // Datos importados automáticamente como variables de plantilla
    'autoimport' => [
        'googleId'          => 'azeazeaez',
        'googleAnalyticsId' => 'azeazeazeaze',
    ],
    // Definición de los contratos usados para la validación de datos
    'validationTypes' => [
        'sitecolor' => 'enum; values: orange, yellow, lime, green',
        'user'      => [
            'type' => 'assoc',
            'keys' => [
                'id?'   => 'int',
                'login' => 'string',
                'email' => 'email',
            ],
        ],
        'category' => '\App\Validations\CategoryValidator',
    ],
    // Configuración extendida
    'x-homepage' => [
        'title'       => 'Título del sitio',
        'description' => 'Descripción del sitio',
    ],
    // Otra configuración extendida
    'x-email' => [
        'senderAddress' => 'admin@localhost.localdomain',
        'senderName'    => 'Administrador',
    ]
];

4Secciones de configuración

4.1application

La sección principal del archivo es la que se llama application. Se usa para definir las variables de configuración más importantes del proyecto.
Estas son las distintas variables que puede contener:

  • dataSources: Se usa para definir los DSN (Data Source Name) de las conexiones a las fuentes de datos (MySQL, Redis, Memcached…).
  • enableSessions: Esta variable debe ponerse a false si no quieres gestionar las sesiones. Esto puede ser útil cuando no quieres rastrear las visitas de los usuarios, como en el caso de una API.
  • sessionName: Nombre de la cookie que contendrá el identificador de sesión.
  • sessionSource: Nombre de la fuente de datos (en el array asociativo dataSources) que contendrá los datos de sesión. Puede ser una conexión Redis, Memcache o SQL (o incluso File o S3). Si este parámetro no está definido, Temma usa el mecanismo nativo de sesiones de PHP.
  • sessionDuration: Duración de la sesión en segundos.
  • sessionSecure: Booleano que indica si la cookie de sesión debe enviarse solo por conexiones seguras mediante HTTPS.
  • cookieDomain: Nombre de dominio asociado a la cookie de sesión. Varias posibilidades:
    • Si el parámetro no está definido, se usa el dominio de nivel 1 del sitio actual (sin subdominios). El navegador enviará la cookie para el dominio y todos sus subdominios (y sub-subdominios, etc.).
      Por ejemplo, si el sitio actual es www.site.com, se usará el dominio site.com. El navegador enviará la cookie (y por tanto la sesión estará accesible) a los sitios site.com, www.site.com, admin.site.com, www.users.site.com, etc.
    • Si el parámetro está definido pero vacío (cadena vacía, false o null), no se asociará ningún dominio a la cookie. El navegador tomará entonces el valor estricto del dominio actual (por ejemplo, admin.user.site.com). La cookie no se enviará a los dominios padre (user.site.com y site.com), ni a los dominios hijos (www.admin.user.site.com).
    • Si el parámetro está definido y no está vacío, este es el valor que se asociará a la cookie. En este caso, la sesión se compartirá para este dominio y todos sus subdominios.
  • defaultNamespace: En caso de que los archivos PHP de los controladores no se guarden en el directorio controlers/, esta variable contiene el namespace por defecto de los controladores.
    Por ejemplo, si esta variable contiene el valor \App\Ctrl, y nos conectamos a la URL www.mysite.com/home, Temma cargará el objeto \App\Ctrl\Home.
  • rootController: Nombre del controlador que se ejecutará cuando se acceda a la raíz del sitio.
  • defaultController: Nombre del controlador que se ejecutará cuando se solicite un controlador que no existe.
  • proxyController: Nombre del controlador que se ejecutará siempre, incluso si el controlador solicitado existe.
  • defaultView: Nombre de la vista por defecto, que se usará para generar el flujo de salida (a menos que se solicite explícitamente otra vista en el controlador o en un plugin).
  • loader: Nombre del objeto que se usará como componente de inyección de dependencias.
  • logFile: Ruta del archivo de log. Pon un valor falso (null, false, cadena vacía o el número cero) para desactivar la escritura en el archivo de log (esto puede ser útil si solo quieres usar un gestor de log; ver la siguiente directiva). Cualquier ruta que no empiece con una barra (/) será relativa a la raíz del proyecto.
  • logManager: Esta variable puede contener el nombre de un objeto de gestión de log, o una lista de nombres de objetos. Consulta la documentación del log para más detalles.

4.2DSN (Data Source Name)

Para definir una conexión a una fuente de datos, debes escribir una cadena de caracteres que contenga todos los parámetros.

Son posibles varios formatos:

  • Variable de entorno
    Es posible indicar el nombre de una variable de entorno.
    env://VAR_NAME
    Esta variable debe estar correctamente definida, y su contenido debe ser un DSN compatible con Temma (ver los casos siguientes).

  • Base de datos relacional
    protocol://user:password@host[:port][/db_name]
    • protocol: Tipo de base de datos, tal como lo define PDO (mysql, pgsql, cubrid, sybase, mssql, dblib, firebird, ibm, informix, sqlsrv, oci, odbc, 4D)
    • user: Nombre de usuario usado para conectarse al servidor
    • password: Contraseña usada para conectarse al servidor
    • host: Nombre de la máquina que aloja el servidor de la base de datos
    • port: Número de puerto de conexión de red (opcional, usa el puerto habitual por defecto)
    • db_name: Nombre de la instancia de base de datos a la que conectarse (opcional)
    Ejemplos:
    • mysql://db_user:db_password@localhost/app_db
    • pgsql://db_user:db_password@db_server.mydomain.com:3307/app_db

    Es posible conectarse a un servidor MySQL usando un socket Unix local, en lugar de un socket de red: mysql://user:password@localhost/db_name#/path/to/unix/socket
    A diferencia de una configuración MySQL clásica, debes añadir un carácter almohadilla (#), seguido de la ruta al archivo de socket Unix. El nombre de la máquina host debe ser localhost. No puede haber número de puerto.
    Ejemplo: mysql://db_user:db_password@localhost/app_db#/var/run/mysqld/mysqld.sock

  • Base de datos relacional local (SQLite)
    sqlite:/path/to/file.sq3

  • Base de datos no relacional (Redis)
    redis://host[:port][/db_number]
    • host: Nombre de la máquina que aloja el servidor Redis
    • port: Número de puerto de conexión de red (opcional, usa el puerto 6379 por defecto)
    • db_number: Número de la base de datos a la que conectarse (opcional, usa la base 0 por defecto)
    Ejemplos:
    • redis://localhost
    • redis://db_server.mydomain.com:6380/2

    Es posible conectarse a un servidor Redis usando un socket Unix local, en lugar de un socket de red:
    redis-sock:///path/to/unix/socket[#base]
    A diferencia de una configuración Redis clásica, debes indicar la ruta al archivo de socket Unix, en lugar del nombre del servidor. Si se especifica un número de base, debe indicarse al final, después de un carácter almohadilla (#); en caso contrario se usa la base 0 por defecto.
    Ejemplos:
    • redis-sock:///var/run/redis/redis-server.sock
    • redis-sock:///var/run/redis/redis-server.sock#2
  • Servidor de caché (Memcached)
    memcache://host[:port]
    • host: Nombre de la máquina que aloja el servidor Memcached
    • port: Número de puerto de conexión de red (opcional, usa el puerto 11211 por defecto)
    Ejemplo: memcache://localhost

    Es posible configurar el cliente para que se conecte a varios servidores Memcached (la distribución de los datos entre varios servidores Memcached se gestiona en el lado del cliente). Para ello, debes indicar los servidores uno tras otro, separados por un punto y coma (;); cada servidor puede ir acompañado de un número de puerto específico.
    Ejemplo: memcache://localhost;serv1:11212;serv2

    Es posible conectarse a un servidor Memcached usando un socket Unix local, en lugar de un socket de red:
    memcache:///path/to/unix/socket
    A diferencia de una configuración Memcached clásica, debes indicar la ruta al archivo de socket Unix, en lugar del nombre del servidor.
    Ejemplo: memcache:///var/run/memcached/memcached.sock

4.3loglevels

La sección loglevels se usa para definir los distintos niveles de log. Encontrarás información sobre su uso en la página de documentación dedicada al log.

Puedes definir varias "clases" de log, cada una con un umbral de visualización diferente. El umbral determina los mensajes que aparecerán en el archivo etc/temma.log. Cuando un código intenta escribir un mensaje de log, dicho mensaje solo aparecerá si su nivel de criticidad es mayor o igual que el del umbral correspondiente.

Los distintos valores posibles del umbral son:

  • DEBUG: mensaje de depuración (criticidad más baja)
  • INFO: mensaje informativo (nivel por defecto para los mensajes cuyo nivel no se especifica)
  • NOTE: notificación; mensaje normal pero significativo (umbral por defecto)
  • WARN: mensaje de alerta; la aplicación no funciona con normalidad pero puede seguir funcionando.
  • ERROR: mensaje de error; la aplicación no funciona con normalidad y debería detenerse.
  • CRIT: mensaje de error crítico; la aplicación corre el riesgo de dañar su entorno (sistema de archivos o base de datos).

Puedes definir tus propias "clases" de log, según las necesidades de tu aplicación. Además de eso, puedes usar las siguientes clases:

  • Temma/Base: Logs relativos a los objetos base de Temma (base de datos, autoloader, sesiones, etc.).
  • Temma/Web: Logs relativos a los objetos del propio framework.
  • default: Se usa para definir el umbral por defecto, para todas las clases que no estén definidas.

Si la directiva loglevels no contiene un array asociativo, sino simplemente una cadena que representa un umbral de log, este se usará para todos los mensajes, sin importar su "clase" específica.


4.4bufferingLoglevels

La sección bufferingLoglevels no es obligatoria. Se usa para modificar el comportamiento del log.

Como vimos antes (ver la sección loglevels), un mensaje aparece en el archivo de log si su nivel de criticidad es mayor o igual que el umbral definido. Si no, simplemente se descarta.

Pero a veces quieres un comportamiento diferente: que los mensajes que no se escriben se almacenen, y que, si finalmente se escribe un mensaje, primero se escriban en el archivo de log todos los mensajes pendientes.

Con la directiva bufferingLoglevels, es posible definir − para cada "clase" de log − el umbral por encima del cual los mensajes no escritos se almacenan para más tarde. Se usa de la misma manera que loglevels, excepto que los mensajes relacionados con una clase no listada no se almacenarán.


4.5routes

Las rutas se usan para definir "controladores virtuales", que sirven de alias para controladores reales. Esto es especialmente útil para permitir el acceso a controladores bajo nombres de archivo (como los clásicos robots.txt y sitemap.xml). Esto también permite que un mismo controlador responda en varias URL diferentes, pudiendo realizar un procesamiento distinto según el nombre de controlador solicitado.


4.6plugins

Los plugins se usan para ejecutar código antes y/o después de la ejecución del controlador. Encontrarás información detallada en la página de documentación dedicada.

La variable _pre se usa para listar los plugins que se ejecutarán antes de los controladores.
La variable _post se usa para listar los plugins que se ejecutarán después de los controladores.

Los controladores pueden listarse por nombre, especificando directivas _pre y _post propias para cada uno. También es posible especificar plugins para una acción particular de un controlador.


4.7errorPages

En algunos casos, tu sitio necesitará enviar un código de error HTTP. Y si ocurre un error en tu propio código (debido a una consulta SQL incorrecta, por ejemplo), el framework decidirá enviar un error 500.

Es posible mostrar una página HTML estática diferente para cada tipo de error HTTP, y definir una página por defecto para todos los tipos de errores que no estén configurados explícitamente.

Para no tener que definir todas las páginas de error posibles, puedes usar la clave "default" para definir la página por defecto.
Si quieres usar la misma página, sea cual sea el error, basta con proporcionar una cadena, no un array asociativo.


4.8includePaths

Esta sección se usa para añadir rutas de inclusión en las que PHP buscará los objetos a incluir.

A diferencia de la directiva phpIncludePaths, las rutas definidas con esta directiva solo son válidas al cargar objetos mediante el autoloader.

Básicamente, Temma añade el directorio lib/ del proyecto a la ruta de inclusión.
Usando el autoloader, si escribes new \Aa\Bb(); PHP probará las siguientes rutas:

  • /path/to/lib1/Aa/Bb.php
  • /path2/Aa/Bb.php
  • lib/Aa/Bb.php

Puede haber ocasiones en las que necesites especificar rutas de inclusión concretas para ciertos prefijos de namespace. Supongamos que tu archivo etc/temma.php contiene la siguiente configuración:

<?php

return [
    'includePaths' => [
        '\Acme\Log' => '/path/to/acme-log/lib',
        '\Aa\\Bb'   => '/path/to/Bb',
    ]
];

Si escribes el código new \Acme\Log\Writer();
PHP intentará cargar el archivo /path/to/acme-log/lib/Writer.php

Por otro lado, si escribes new \Aa\Bb\Cc();
PHP cargará el archivo /path/to/Bb/Cc.php

Es posible especificar rutas de inclusión con y sin namespace al mismo tiempo:

<?php

return [
    'includePaths' => [
        '/path/to/lib1',
        '/path2',
        '\Acme\Log' => '/path/to/acme-log/lib',
        '\Aa\\Bb'   => '/path/to/Bb',
    ]
];

4.9phpIncludePaths

Esta sección se usa para añadir rutas de inclusión en las que PHP buscará los archivos a incluir.

Básicamente, Temma añade el directorio lib/ del proyecto a la ruta de inclusión. Así, si escribes en tu código: include('Toto.php');
Se cargará el archivo lib/Toto.php.

Pero si añades la siguiente configuración en tu archivo etc/temma.php:

<?php

return [
    'includePaths' => [
        '/path/to/lib1',
        '/path2',
    ]
];

Si escribes en tu código require_once('foo.php');
PHP intentará cargar sucesivamente varias rutas, hasta encontrar el archivo. En orden, probará:

  • /path/to/lib1/foo.php
  • /path2/foo.php
  • lib/foo.php

4.10autoimport

Todas las directivas colocadas en la sección autoimport se cargan automáticamente en la variable de plantilla $conf.

Se puede acceder a las variables de plantilla en los controladores escribiendo $this['variable'].
También se pueden crear (o sobrescribir) escribiendo $this['variable'] = $value;.


4.11validationTypes

Esta sección se usa para declarar tipos que se pueden usar para la validación de datos. A estos tipos se les pueden asociar contratos de validación (ver la documentación del objeto DataFilter) u objetos de validación.

Los tipos definidos se pueden usar luego en los métodos de validación del objeto Request, en los atributos de validación, o en el objeto DataFilter.


5Configuración extendida

El archivo de configuración puede contener secciones de configuración extendida. Su objetivo es agrupar variables de parametrización adicionales, clasificándolas por funcionalidad. Así, un controlador o un plugin puede recuperar fácilmente los valores que le interesan, sin necesidad de conocer todas las variables de configuración existentes.

Para recuperar una sección completa de configuración extendida, hay que pasar por el objeto de configuración disponible directamente en los controladores:

$this->_config

Y mediante el componente de inyección de dependencias:

$this->_loader->config

Por ejemplo, para recuperar toda la configuración extendida x-homepage, hay que escribir:

$data = $this->_config->xtra('homepage');

Para recuperar la variable senderName de la sección x-email:

$data = $this->_config->xtra('email', 'senderName');

También es posible especificar un valor por defecto que se usará si la variable solicitada no existe:

$data = $this->_config->xtra('email', 'recipient', 'contact@host.domain');

6Configuración por plataforma y sobrescritura de configuración

En lugar de tener un único archivo etc/temma.php (o etc/temma.json, etc.), puedes usar archivos diferentes según la plataforma en la que se despliega el sitio: local, test, staging, producción...

Para ello, debes definir una variable de entorno ENVIRONMENT, cuyo valor sea el nombre de la plataforma actual.

Por ejemplo, podrías tener un archivo etc/temma.test.php y un archivo etc/temma.prod.php. El primero se usará en los servidores cuya variable de entorno ENVIRONMENT valga test, y el segundo en los servidores cuya variable valga prod.

A menudo, solo ciertas partes de la configuración difieren de una plataforma a otra. En ese caso, puedes poner toda la configuración común en el archivo habitual etc/temma.php, y colocar las configuraciones específicas de cada plataforma en los archivos específicos.
El archivo general se lee primero, y el archivo específico de la plataforma lo sobrescribe con los valores que contiene.

Además, los archivos no tienen por qué estar todos en el mismo formato. Puedes tener un archivo general etc/temma.php, y sobrescribirlo en staging con un archivo etc/temma.staging.json y en producción con un archivo etc/temma.prod.yaml.

Por último, puede ser necesario sobrescribir el archivo de configuración añadiendo elementos al principio de una lista definida (por ejemplo, para añadir un plugin al principio de la lista de preplugins). En ese caso, añade el sufijo __prepend (que empieza con dos guiones bajos) a la clave de configuración.
Por ejemplo:

<?php

return [
    'plugins' => [
        '_pre__prepend' => [
            // este plugin será el primero de la lista '_pre'
            'MyFirstPlugin'
        ]
    ]
];