Search by

team-mate-pro / infra-bundle

teammatepro

Infrastructure verification bundle for Symfony applications

Package info

github.com/team-mate-pro/infra-bundle

Type:symfony-bundle

pkg:composer/team-mate-pro/infra-bundle

Statistics

Installs: 5 843

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

2.2.1 2026-09-22 11:16 UTC

This package is auto-updated.

Last update: 2026-09-22 11:20:23 UTC


README

Version: 1.5.0 Type: symfony-bundle

Infrastructure verification bundle for Symfony applications. Provides a base command for verifying server configuration before deployment.

Installation

composer require team-mate-pro/infra-bundle:^1.0

Configuration

Register the bundle in config/bundles.php:

return [
    // ...
    TeamMatePro\InfraBundle\TeamMateProInfraBundle::class => ['all' => true],
];

Usage

Create a verification command by extending AbstractInfraVerifyCommand:

<?php

declare(strict_types=1);

namespace App\Command;

use Symfony\Component\Console\Attribute\AsCommand;
use TeamMatePro\InfraBundle\Command\AbstractInfraVerifyCommand;

#[AsCommand(
    name: 'app:infra:verify',
    description: 'Verify infrastructure configuration',
)]
final class InfraVerifyCommand extends AbstractInfraVerifyCommand
{
    protected function verify(): void
    {
        // Add your verification logic here
    }
}

Run the command:

php bin/console app:infra:verify

Available Verifiers

section(string $title)

Creates a visual section in the output.

$this->section('PHP Extensions');

verifyPhpVersion(?string $projectDir = null)

Checks the running PHP version against the require.php constraint from the project's composer.json. Only the lowest version in the constraint is compared, which covers >=8.3, ^8.3 and >=8.3 <9.

$this->verifyPhpVersion();                  // composer.json in the current working directory
$this->verifyPhpVersion($this->projectDir); // explicit project directory

verifyPhpExtension(string $name, ?string $description = null)

Checks if a PHP extension is loaded.

$this->verifyPhpExtension(name: 'pdo_mysql');
$this->verifyPhpExtension(name: 'gd', description: 'image processing');

verifyPhpIniSetting(string $name, string $expectedValue, ?string $description = null)

Checks if a PHP ini setting has the expected value. Normalizes boolean-like values (On/Off/1/0/true/false).

// Sentry - stack trace arguments
$this->verifyPhpIniSetting('zend.exception_ignore_args', 'Off', 'Sentry stack trace arguments');

// Memory limit
$this->verifyPhpIniSetting('memory_limit', '256M');

// Display errors off in production
$this->verifyPhpIniSetting('display_errors', 'Off', 'must be disabled in production');

verifyEnvVariable(string $name, ?string $description = null, ?string $expectedValue = null)

Checks if an environment variable is set. Optionally validates expected value.

// Check if set
$this->verifyEnvVariable(name: 'APP_SECRET');

// Check with description
$this->verifyEnvVariable(name: 'DATABASE_URL', description: 'database connection');

// Check exact value
$this->verifyEnvVariable(
    name: 'APP_ENV',
    expectedValue: 'prod',
);

// Full example
$this->verifyEnvVariable(
    name: 'SSO_AUTH_URL',
    description: 'frontend redirects here for login',
    expectedValue: 'https://login.example.com/',
);

verifyBinary(array $command, ?string $description = null)

Checks if a system binary is available and executable.

$this->verifyBinary(
    command: ['/usr/bin/wkhtmltopdf', '--version'],
    description: 'PDF generation',
);

$this->verifyBinary(
    command: ['node', '--version'],
    description: 'Node.js runtime',
);

verifyDatabaseConnection(int $timeoutSeconds = 5)

Verifies MySQL database connection using DATABASE_URL environment variable.

Note: If ext-pdo is not loaded, shows [WARN] and skips the check instead of failing.

$this->verifyDatabaseConnection();
$this->verifyDatabaseConnection(timeoutSeconds: 10);

verifyHttpConnection(string $url, ?string $description = null, int $expectedStatusCode = 200, int $timeoutSeconds = 5)

Checks HTTP endpoint and validates response status code.

$this->verifyHttpConnection(
    url: 'https://api.example.com/health',
    description: 'API health check',
);

$this->verifyHttpConnection(
    url: 'https://api.example.com/status',
    expectedStatusCode: 204,
    timeoutSeconds: 10,
);

verifyHttpConnectionHandshake(string $url, ?string $description = null, int $timeoutSeconds = 5)

Checks if HTTP endpoint is reachable (any response is OK). Use this when you only need to verify network connectivity.

$this->verifyHttpConnectionHandshake(
    url: 'https://login.example.com/oauth2/jwks',
    description: 'SSO JWKS endpoint',
);

$this->verifyHttpConnectionHandshake(
    url: 'https://external-api.example.com/',
    description: 'External API',
    timeoutSeconds: 10,
);

verifyCorsHeaders(string $url, string $origin, array $expectedHeaders = ['Access-Control-Allow-Origin'], string $method = 'OPTIONS', array $requestHeaders = [], int $timeoutSeconds = 5)

Probes a URL with an Origin request header and asserts the expected CORS response headers are present and well-formed. Detects three common misconfigurations: a missing header, a header returned more than once (e.g. both nginx and the app append it), and a single header value with merged, comma-separated values. Pass the response headers you want to assert via $expectedHeaders.

For a preflight check use method: 'OPTIONS' (the default) — the standard Access-Control-Request-Method/Access-Control-Request-Headers request headers are added automatically unless overridden via $requestHeaders. For a simple request use method: 'GET' or 'HEAD'.

// Preflight check for a public form endpoint
$this->verifyCorsHeaders(
    url: 'https://example.com/public/forms/00000000-0000-0000-0000-000000000000',
    origin: 'https://example.com',
);

// Simple request, asserting several CORS headers at once
$this->verifyCorsHeaders(
    url: 'https://example.com/assets/form.js',
    origin: 'https://example.com',
    expectedHeaders: [
        'Access-Control-Allow-Origin',
        'Access-Control-Allow-Methods',
        'Access-Control-Allow-Headers',
    ],
    method: 'HEAD',
);

verifyEncryptedZipArchive(array $files = [], ?string $description = null)

Verifies that the server can actually produce a password-protected ZIP archive. A loaded ext-zip proves nothing about encryption — that additionally requires libzip built with encryption support, and the gap only surfaces when an archive is written. The check packs the given files with AES-256 and a random password, re-opens the archive and asserts that no entry is readable without the password and that every entry decrypts to its original content. Everything happens in a temporary directory that is removed afterwards.

Pass $files as entry name in the archive => path of an existing file. Leave it empty to pack a self-generated probe pair — enough to prove the capability without touching application data.

// Capability check only
$this->verifyEncryptedZipArchive();

// Pack real documents, e.g. the attachments a mailing feature ships
$this->verifyEncryptedZipArchive(
    files: [
        'referral.pdf' => '/var/www/app/var/samples/referral.pdf',
        'attachments.csv' => '/var/www/app/var/samples/attachments.csv',
    ],
    description: 'Email attachment archive',
);

Example Command

<?php

declare(strict_types=1);

namespace App\Command;

use Symfony\Component\Console\Attribute\AsCommand;
use TeamMatePro\InfraBundle\Command\AbstractInfraVerifyCommand;

#[AsCommand(
    name: 'app:infra:verify',
    description: 'Verify infrastructure configuration',
)]
final class InfraVerifyCommand extends AbstractInfraVerifyCommand
{
    protected function verify(): void
    {
        $this->section('PHP Extensions');
        $this->verifyPhpExtension(name: 'pdo_mysql', description: 'database');
        $this->verifyPhpExtension(name: 'intl', description: 'internationalization');

        $this->section('Environment Variables');
        $this->verifyEnvVariable(name: 'APP_ENV', expectedValue: 'prod');
        $this->verifyEnvVariable(name: 'APP_SECRET');
        $this->verifyEnvVariable(name: 'DATABASE_URL');

        $this->section('System Binaries');
        $this->verifyBinary(
            command: ['/usr/bin/wkhtmltopdf', '--version'],
            description: 'PDF generation',
        );

        $this->section('Services');
        $this->verifyDatabaseConnection();

        $this->section('External Connections');
        $this->verifyHttpConnectionHandshake(
            url: 'https://api.example.com/',
            description: 'External API',
        );
    }
}

Output Example

Infrastructure Verification
===========================

PHP Extensions
--------------
[OK] ext-pdo_mysql (database)
[OK] ext-intl (internationalization)

Environment Variables
---------------------
[OK] APP_ENV
[OK] APP_SECRET
[OK] DATABASE_URL

System Binaries
---------------
[OK] /usr/bin/wkhtmltopdf (PDF generation)

Services
--------
[OK] MySQL connection

External Connections
--------------------
[OK] https://api.example.com/ (External API)

 [OK] All infrastructure checks passed

Requirements

  • PHP 8.3+ (tested on 8.3, 8.4 and 8.5)
  • Symfony 7.0+

Development

This package follows the TeamMatePro quality standards: PSR-12 (PHP_CodeSniffer), PHPStan at max level, and a PHPUnit test suite. Everything runs in Docker, so no local PHP extensions are required.

# Start / stop the dev container
make start
make stop

# Run the full CI gate locally (phpcs + phpstan + unit tests)
make check

# Auto-fix coding-standard issues
make fix

The same checks are also available as Composer scripts and are what CI runs via the shared GitLab templates (docker-compose.gitlab.yml):

composer phpcs        # PSR-12 coding standard
composer phpstan      # static analysis (level max)
composer tests:unit   # PHPUnit unit suite

Layout

Path Purpose
src/ bundle source (AbstractInfraVerifyCommand, the bundle class)
tests/Unit/ unit tests mirroring src/
tests/_Data/ test fixtures (the concrete command subclass, HTTP router)
tests/App/ minimal kernel used by container-aware tests
docker/, docker-compose*.yml local + CI containers
phpcs.xml, phpstan.neon, phpunit.xml quality-tool configuration