Kategorien
Symfony

Symfony mögliche Konfigurationsparameter anzeigen

Um eine alle möglichen Config Parameter eines Packetes anzuzeigen, kann man folgenden Konsolenbefehl verwenden:

php bin/console config:dump-reference doctrine_migrations

Ausgabe:

# Default configuration for extension with alias: "doctrine_migrations"
doctrine_migrations:

    # A list of namespace/path pairs where to look for migrations.
    migrations_paths:

        # Prototype
        namespace:            ~

    # A set of services to pass to the underlying doctrine/migrations library, allowing to change its behaviour.
    services:

        # Prototype
        service:              ~

    # A set of callables to pass to the underlying doctrine/migrations library as services, allowing to change its behaviour.
    factories:

        # Prototype
        factory:              ~

    # Storage to use for migration status metadata.
    storage:

        # The default metadata storage, implemented as a table in the database.
        table_storage:
            table_name:           null
            version_column_name:  null
            version_column_length: null
            executed_at_column_name: null
            execution_time_column_name: null

    # A list of migrations to load in addition to the one discovered via "migrations_paths".
    migrations:           []

    # Connection name to use for the migrations database.
    connection:           null

    # Entity manager name to use for the migrations database (available when doctrine/orm is installed).
    em:                   null

    # Run all migrations in a transaction.
    all_or_nothing:       false

    # Adds an extra check in the generated migrations to allow execution only on the same platform as they were initially generated on.
    check_database_platform: true

    # Custom template path for generated migration classes.
    custom_template:      null

    # Organize migrations mode. Possible values are: "BY_YEAR", "BY_YEAR_AND_MONTH", false
    organize_migrations:  false

    # Use profiler to calculate and visualize migration status.
    enable_profiler:      false

    # Whether or not to wrap migrations in a single transaction.
    transactional:        true
Kategorien
PHP

composer ohne Memory Limit ausführen

COMPOSER_MEMORY_LIMIT=-1 composer update
Kategorien
PHP Symfony

PHP Anwendungen automatisiert modernisieren mit rector

Das Tool rector ist eine Konsolenanwendung für automatisiertes Refactoring von PHP Anwendungen.

Damit lassen sich schnell einfache Altlasten entfernen aus Legacy Projekten.

Mit dem folgenden Rector Script (./rector.php) können die gängigsten Refactorings durchgeführt werden:

<?php

declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\SetList;

return static function (RectorConfig $rectorConfig): void {
    $setLists = [
        SetList::PHP_52,
        SetList::PHP_53,
        SetList::PHP_54,
        SetList::PHP_55,
        SetList::PHP_56,
        SetList::PHP_70,
        SetList::PHP_71,
        SetList::PHP_72,
        SetList::PHP_73,
        SetList::PHP_74,
        SetList::PHP_80,
        SetList::PHP_81,
        SetList::ACTION_INJECTION_TO_CONSTRUCTOR_INJECTION,
        SetList::CODE_QUALITY,
        SetList::CODING_STYLE,
        SetList::DEAD_CODE,
        SetList::EARLY_RETURN,
        SetList::GMAGICK_TO_IMAGICK,
        SetList::MONOLOG_20,
        SetList::MYSQL_TO_MYSQLI,
        SetList::NAMING,
        SetList::PRIVATIZATION,
        SetList::TYPE_DECLARATION,
        SetList::TYPE_DECLARATION_STRICT,
    ];

    foreach($setLists as $setList) {

        $rectorConfig->import(
            $setList
        );
    }

    $rectorConfig->paths([
        __DIR__.'/src',
        __DIR__.'/tests',
    ]);
};

ausführbar im Dry mode ohne Änderungen am Code mit:

vendor/bin/rector process --debug --dry-run

Kategorien
Docker Symfony

SSO Saml Login mit Symfony

Single-Sign-On ist das Thema der Stunde, es verbessert die Sicherheit und auch die User Verwaltung. Um Saml SSO in Symfony einzubauen gibt es das OneloginSaml Bundle, ein Symfony Wrapper für das php-saml Packet.

Der Vorteil für die Benutzer von SSO liegt darin, dass diese sich morgens einmal authentifizieren müssen und danach den ganzen Tag alle Anwendungen verwenden können ohne sich nochmals einloggen zu müssen.

Der Vorteil für das Unternehmen liegt darin, dass es vorgeben kann, dass die Authentifierung nur mit 2 Faktor Authentifizierung stattfinden kann (z.B. Handy App und Passwort), um mehr Sicherheit zu gewährleisten. Dies kannn in der Zukunft den steigendenden Sicherheitsbedürfnissen angepasst werden. Außerdem können zentral Rechte vergeben werden für User und nach dem Austritt aus dem Unternehmen einfach wieder entzogen werden.

Wenn das Bundle installiert und korrekt konfiguriert ist, benötigt man eine Dev-Umgebung mit einem Identity Provider (IdP). Dafür bietet sich docker-simplesamlphp an:

docker-compose.yml:

version: '3.7'
services:
	idp:
		image: kenchan0130/simplesamlphp
		container_name: idp
		ports:
			- "8080:8080"
		environment:
			SIMPLESAMLPHP_SP_ENTITY_ID: http://localhost/saml/metadata
			SIMPLESAMLPHP_SP_ASSERTION_CONSUMER_SERVICE: http://localhost/saml/acs
			SIMPLESAMLPHP_SP_SINGLE_LOGOUT_SERVICE: http://localhost/saml/logout

Das OneloginSaml Bundle muss folgender Maßen konfiguriert werden:

hslavich_onelogin_saml:
  # Basic settings
  idp:
    # Identifier of the SP entity  (must be a URI)
    entityId: 'http://localhost:8080/simplesaml/saml2/idp/metadata.php'
    singleSignOnService:
      url: 'http://localhost:8080/simplesaml/saml2/idp/SSOService.php'
      binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
    singleLogoutService:
      url: 'http://localhost:8080/simplesaml/saml2/idp/SingleLogoutService.php'
      binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
    x509cert: 'insert certificate here'
  # Service Provider Data that we are deploying.
  sp:
    entityId: 'http://localhost/saml/metadata'
    assertionConsumerService:
      url: 'http://localhost/saml/acs'
      binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
    singleLogoutService:
      url: 'http://localhost/saml/logout'
      binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
    privateKey: ''

Die Informationen kann man hier ablesen, auch die vom Zertifikat:

SAML Metadata: http://localhost:8080/simplesaml/saml2/idp/metadata.php?output=xhtml

Wenn man alles richtig konfiguriert hat, dann landet man beim Aufruf der Symfony Anwendung auf der Login Seite des IdP Providers und kann sich dort einloggen:

Login Seite des IdP Providers

Die Logins lauten:

admin:     secret
user1:     password
user2:     password

Dann sollte man sich mit seiner Symfony Applikation lokal über SAML einloggen und testen können.

Kategorien
PHP

PHP-FFMpeg resize Video

Um mit PHP-FFMpeg ein Video auf eine maximale Größe und Breite zu resizen kann man folgenden Code verwenden:

<?php

namespace App\Core\Media;

use FFMpeg\Coordinate\Dimension;
use FFMpeg\FFMpeg;
use FFMpeg\Filters\Video\ResizeFilter;
use FFMpeg\Format\Video\X264;
use FFMpeg\Media\Video;

class VideoManager
{

    /**
     * @var FFMpeg
     */
    private $ffmpeg;

    /**
     * @var int
     */
    private $videoMaxHeight;

    /**
     * @var int
     */
    private $videoMaxWidth;

    /**
     * @var Video|null
     */
    private $video;

    /**
     * @var string|null
     */
    private $videoPath;

    /**
     * @param FFMpeg $ffmpeg
     * @param int $videoMaxHeight
     * @param int $videoMaxWidth
     */
    public function __construct(
        FFMpeg $ffmpeg,
        int $videoMaxHeight,
        int $videoMaxWidth
    )
    {
        $this->ffmpeg = $ffmpeg;
        $this->videoMaxHeight = $videoMaxHeight;
        $this->videoMaxWidth = $videoMaxWidth;
    }

    /**
     * @param string|null $videoPath
     * @return VideoManager
     */
    public function setVideoPath(?string $videoPath): VideoManager
    {
        $this->videoPath = $videoPath;
        if (!file_exists($videoPath)) {
            throw new \Exception('video does not exists:' . $videoPath);
        }
        $this->video = $this->ffmpeg->open($videoPath);

        return $this;
    }



    /**
     * @return int
     */
    public function getHeight(): int
    {
        return $this->video->getStreams()->first()->getDimensions()->getHeight();
    }

    /**
     * @return int
     */
    public function getWidth(): int
    {
        return $this->video->getStreams()->first()->getDimensions()->getWidth();
    }

    /**
     * @return void
     */
    public function formatH264Codec()
    {
        $format = new X264();

        $tmpDirectory = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'video_format' . DIRECTORY_SEPARATOR;
        if (!is_dir($tmpDirectory)) {
            mkdir($tmpDirectory);
        }

        $tmpFilePath = $tmpDirectory . uniqid(time()) . '.mp4';

        $this->resizeVideo();

        $format
            ->setVideoCodec('libx264')
         ;

        $this->video->save($format, $tmpFilePath);

        // replace with encoded file
        unlink($this->videoPath);
        copy($tmpFilePath, $this->videoPath);

        //reset ffmpeg
        $this->ffmpeg = FFMpeg::create();
        $this->video = $this->ffmpeg->open($this->videoPath);
    }

    /**
     * @return void
     */
    protected function resizeVideo()
    {
        $width  = $this->getWidth();
        $height = $this->getHeight();

        if ($width > $this->videoMaxWidth || $height > $this->videoMaxHeight) {
            $factorWidth = $width / $this->videoMaxWidth;
            $factorHeight = $height / $this->videoMaxHeight;

            if ($factorWidth > $factorHeight) {
                $width = $width / $factorWidth;
                $height = $height / $factorWidth;
                $mode = ResizeFilter::RESIZEMODE_SCALE_WIDTH;
            } else {
                $width = $width / $factorHeight;
                $height = $height / $factorHeight;
                $mode = ResizeFilter::RESIZEMODE_SCALE_HEIGHT;
            }

            //round to the nearest odd number: ffmpeg otherwise throws an error
            $height = $this->roundToNextOddNumber($height);
            $width  = $this->roundToNextOddNumber($width);

            $this->video->filters()->resize(
                new Dimension($width, $height),
                $mode
            );
        }
    }

    /**
     * @param float $number
     * @return int
     */
    private function roundToNextOddNumber(float $number): int
    {
        $number = ceil($number); // Round up decimals to an integer
        if($number % 2 == 1) $number++;

        return $number;
    }
}

Usage:

$videoManager = new VideoManager(FFMpeg\FFMpeg::create(), 200, 300);
$videoManager->setVideoPath('/path/to/video.dat');
$this->videoManager->formatH264Codec();
Kategorien
PHP Symfony Testing

Symfony System-Test für valide Konfiguration der Umgebungen

Es gibt in Symfony die Möglichkeit mehrere Umgebungen (Environments) anzulegen und verschiedenartig zu konfigurieren. Das Problem ist, dass die automatisierten Tests immer gegen die Test-Umgebung und somit auch gegen die Test-Konfiguration gemacht werden.

Wie kann man sicher gehen, dass die produktive Konfiguration valide ist und dies mit einem automatisierten Test absichern?

Die Antwort ist sehr einfach, sollte eine Umgebungs-Konfiguration syntaktisch falsch sein, schlägt der Test fehl:

<?php

namespace App\Tests\System;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class BootKernelTest extends WebTestCase
{

    /**
     * @return array
     */
    public function dataProviderTestBootKernel(): array
    {
        return [
            'dev' => ['dev'],
            'test' => ['test'],
            'prod' => ['prod'],
        ];
    }

    /**
     * the test would fail if the environment config is wrong
     *
     * @dataProvider dataProviderTestBootKernel
     *
     * @param string $environment
     * @return void
     */
    public function testBootKernel(string $environment)
    {
        $kernel = $this->bootKernel(['environment' => $environment]);

        $this->assertNotEmpty($kernel);
    }

}

Kategorien
Amazon AWS WordPress

WordPress Bitnami Banner entfernen auf AWS AMI

Um den Bitnami Banner entfernen zu können, soll man per SSH einen Konsolenbefehl ausführen:

sudo /opt/bitnami/apps/wordpress/bnconfig --disable_banner 1

dies führte bei mir zu einer Fehlermeldung, dass die Datei bnconfig nicht vorhanden ist:

sudo: /opt/bitnami/apps/wordpress/bnconfig: command not found

Die Ursache ist, dass die Datei standradmäßig erst deaktiviert ist, es gibt eine /opt/bitnami/apps/wordpress/bnconfig.disabled

Diese muss aktiviert werden, indem sie umbenannt wird nach bnconfig:

sudo mv /opt/bitnami/apps/wordpress/bnconfig.disabled /opt/bitnami/apps/wordpress/bnconfig

Danach kann der Banner erfolgreich entfernt werden:

sudo /opt/bitnami/apps/wordpress/bnconfig --disable_banner 1
Kategorien
Symfony

Symfony Cache mit Datenbank

Die Symfony Cache Komponente besitzt eine große Zahl von möglichen Adaptern zum Cachen von Informationen.

Zum Cachen in der Datenbank kann man den folgenden PdoAdapter für Doctrine verwenden und folgender Maßen konfigurieren:

#config/packages/framework.yaml
framework:
    cache:
        pools:
            app:
                adapter: AppCache
#config/services.yaml
AppCache:
  class: Symfony\Component\Cache\Adapter\PdoAdapter
  arguments:
    - '@doctrine.dbal.default_connection'

Damit erzeugt Symfony eine Datenbank Tabelle innerhalb der default Datenbank Verbindung für die Cache Einträge.

Dies hat besonders Performancegewinne und ist übersichtlicher im Vergleich zu  der Default-Filesystem-Variante. Besonders ist dies für eine Docker Umgebung zu empfehlen, weil das langsame Dateisystem nicht verwnedet werden muss.

Kategorien
Symfony

Symfony 5 Exception „The annotation was never imported.“

Ein Grund der Fehlermeldung kann sein, dass es tatsächlich eine Annotation ist, die ungültig ist, sich aber im vendor Code befindet. Dann sollte man diese ignorieren.

Fehlermeldung:

Doctrine\Common\Annotations\AnnotationException : [Semantical Error] The annotation "@suppress" in method Monolog\Formatter\LineFormatter::normalizeException() was never imported. Did you maybe forget to add a "use" statement for this annotation?

Sollte man die Annotation ignorieren mit dem statischen Aufruf von in der config/bootstrap.php:

AnnotationReader::addGlobalIgnoredName('suppress');

 

Kategorien
Amazon AWS PHP

PHP eigene Metriken in AWS Cloudwatch erstellen

Um in CloudWatch eigene Metriken anzulegen, um z.B. Alarm auszulösen und Monitoring zu betreiben mit den Daten der Applikation, kann man mit dem aws-php-sdk den folgenden Code verwenden:

 $client = new CloudWatchClient([
        'region' => 'eu-central-1',
        'version' => '2010-08-01',
        'credentials' => [
            'key'    => 'xxx',
            'secret' => 'xxx
        ],
    ]);
}
$result = $this->client->putMetricData(array(

    'Namespace' => 'MyProject',
    'MetricData' => array(
        array(
            'MetricName' => 'MyMetric,
            'Timestamp' => time(),
            'Value' => 1,
            'Unit' => 'Count',
            'Dimensions' => array(
                array(
                    'Name' => 'Environment',
                    'Value' => 'test',
                ),
            ),
        )
    )
));