From 36cb3f13aceae4402f88b2386f8f86895ec4f307 Mon Sep 17 00:00:00 2001 From: johbuch Date: Wed, 25 Mar 2026 20:56:59 +0100 Subject: [PATCH 1/4] feat(compatibility): update compatibility with php 8.4, symfony 8 and easy admin 5 --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index dcebf0a..9bd2a4a 100644 --- a/composer.json +++ b/composer.json @@ -11,8 +11,8 @@ ], "require": { "php": ">=8.0", - "oliverde8/php-etl-bundle": "^1.1", - "easycorp/easyadmin-bundle": "^4.0" + "oliverde8/php-etl-bundle": "^v2.0.0-alpha1", + "easycorp/easyadmin-bundle": "^5.0" }, "autoload": { "psr-4": { From 29c9c8e6310e8dab455f9dfe30d58b94413e4b4c Mon Sep 17 00:00:00 2001 From: johbuch Date: Tue, 21 Apr 2026 09:51:56 +0200 Subject: [PATCH 2/4] chore(rector): fix code with rector with php8.2 --- Controller/Admin/EtlDashboardController.php | 7 +--- .../Admin/EtlDownloadFileController.php | 13 ++---- .../Admin/EtlExecutionCrudController.php | 41 ++++++++----------- .../Oliverde8PhpEtlEasyAdminExtension.php | 3 +- Oliverde8PhpEtlEasyAdminBundle.php | 2 + 5 files changed, 25 insertions(+), 41 deletions(-) diff --git a/Controller/Admin/EtlDashboardController.php b/Controller/Admin/EtlDashboardController.php index 17cbfc8..68de9d2 100644 --- a/Controller/Admin/EtlDashboardController.php +++ b/Controller/Admin/EtlDashboardController.php @@ -12,16 +12,11 @@ class EtlDashboardController extends AbstractController { - protected EtlExecutionRepository $etlExecutionRepository; - /** * EtlDashboardController constructor. - * - * @param EtlExecutionRepository $etlExecutionRepository */ - public function __construct(EtlExecutionRepository $etlExecutionRepository) + public function __construct(protected EtlExecutionRepository $etlExecutionRepository) { - $this->etlExecutionRepository = $etlExecutionRepository; } diff --git a/Controller/Admin/EtlDownloadFileController.php b/Controller/Admin/EtlDownloadFileController.php index 8c9e605..25cbfc6 100644 --- a/Controller/Admin/EtlDownloadFileController.php +++ b/Controller/Admin/EtlDownloadFileController.php @@ -17,15 +17,8 @@ class EtlDownloadFileController extends AbstractController { - /** @var ExecutionContextFactory */ - protected $executionContextFactory; - - /** - * @param ExecutionContextFactory $executionContextFactory - */ - public function __construct(ExecutionContextFactory $executionContextFactory) + public function __construct(protected \Oliverde8\PhpEtlBundle\Services\ExecutionContextFactory $executionContextFactory) { - $this->executionContextFactory = $executionContextFactory; } /** @@ -39,14 +32,14 @@ public function index(EtlExecution $execution, string $filename): Response $context = $this->executionContextFactory->get(['etl' => ['execution' => $execution]]); $file = $context->getFileSystem()->readStream($filename); - $response = new StreamedResponse(function () use ($file) { + $response = new StreamedResponse(function () use ($file): void { $outputStream = fopen('php://output', 'wb'); stream_copy_to_stream($file, $outputStream); }); $disposition = HeaderUtils::makeDisposition( HeaderUtils::DISPOSITION_ATTACHMENT, - "execution-{$execution->getName()}-{$execution->getId()}-" . $filename + sprintf('execution-%s-%s-', $execution->getName(), $execution->getId()) . $filename ); $response->headers->set('Content-Disposition', $disposition); diff --git a/Controller/Admin/EtlExecutionCrudController.php b/Controller/Admin/EtlExecutionCrudController.php index afe9185..02e32ef 100644 --- a/Controller/Admin/EtlExecutionCrudController.php +++ b/Controller/Admin/EtlExecutionCrudController.php @@ -24,23 +24,8 @@ class EtlExecutionCrudController extends AbstractCrudController { - /** @var ExecutionContextFactory */ - protected $executionContextFactory; - - /** @var ChainProcessorsManager */ - protected $chainProcessorManager; - - /** @var AdminUrlGenerator */ - protected $adminUrlGenerator; - - public function __construct( - ExecutionContextFactory $executionContextFactory, - ChainProcessorsManager $chainProcessorManager, - AdminUrlGenerator $adminUrlGenerator - ) { - $this->executionContextFactory = $executionContextFactory; - $this->chainProcessorManager = $chainProcessorManager; - $this->adminUrlGenerator = $adminUrlGenerator; + public function __construct(protected \Oliverde8\PhpEtlBundle\Services\ExecutionContextFactory $executionContextFactory, protected \Oliverde8\PhpEtlBundle\Services\ChainProcessorsManager $chainProcessorManager, protected \EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator $adminUrlGenerator) + { } @@ -62,6 +47,7 @@ public function configureActions(Actions $actions): Actions if (!$this->isGranted(EtlExecutionVoter::QUEUE, EtlExecution::class)) { $actions->remove(Crud::PAGE_INDEX, Action::NEW); } + if (!$this->isGranted(EtlExecutionVoter::VIEW, EtlExecution::class)) { $actions->remove(Crud::PAGE_INDEX, Action::DETAIL); } @@ -98,14 +84,14 @@ public function configureFields(string $pageName): iterable CodeEditorField::new('definition')->setTemplatePath('@Oliverde8PhpEtlEasyAdmin/fields/code_editor.html.twig'), FormField::addPanel('Execution outpus')->addCssClass("col-12"), - TextField::new('Files')->formatValue(function ($value, EtlExecution $entity) { + TextField::new('Files')->formatValue(function ($value, EtlExecution $entity): array { $urls = []; if ($this->isGranted(EtlExecutionVoter::DOWNLOAD, EtlExecution::class)) { $context = $this->executionContextFactory->get(['etl' => ['execution' => $entity]]); $files = $context->getFileSystem()->listContents("/"); foreach ($files as $file) { - if (strpos($file, '.') !== 0) { + if (!str_starts_with($file, '.')) { $url = $this->adminUrlGenerator ->setRoute("etl_execution_download_file", ['execution' => $entity->getId(), 'filename' => $file]) ->generateUrl(); @@ -119,7 +105,7 @@ public function configureFields(string $pageName): iterable })->setTemplatePath('@Oliverde8PhpEtlEasyAdmin/fields/files.html.twig'), CodeEditorField::new('errorMessage')->setTemplatePath('@Oliverde8PhpEtlEasyAdmin/fields/code_editor.html.twig'), - TextField::new('Logs')->formatValue(function ($value, EtlExecution $entity) { + TextField::new('Logs')->formatValue(function ($value, EtlExecution $entity): array { $context = $this->executionContextFactory->get(['etl' => ['execution' => $entity]]); $logs = []; if ($context->getFileSystem()->fileExists("execution.log")) { @@ -129,16 +115,18 @@ public function configureFields(string $pageName): iterable $logs[] = $line; $i++; } + fclose($file); } $url = ""; $moreLogs = false; - if (!empty($logs)) { + if ($logs !== []) { $url = $this->adminUrlGenerator ->setRoute("etl_execution_download_file", ['execution' => $entity->getId(), 'filename' => 'execution.log']) ->generateUrl(); } + if (count($logs) > 100) { $moreLogs = true; } @@ -152,6 +140,7 @@ public function configureFields(string $pageName): iterable ]; } + if (Crud::PAGE_INDEX === $pageName) { return [ Field::new('id'), @@ -163,6 +152,7 @@ public function configureFields(string $pageName): iterable Field::new('endTime'), ]; } + if (Crud::PAGE_NEW === $pageName) { return [ ChoiceField::new('name', 'Chain Name') @@ -200,11 +190,11 @@ public function configureFilters(Filters $filters): Filters ->add('endTime'); } - public function createEntity(string $entityFqcn) + public function createEntity(string $entityFqcn): object { $user = $this->getUser(); $username = null; - if ($user) { + if ($user instanceof \Symfony\Component\Security\Core\User\UserInterface) { $username = $user->getUsername(); } @@ -219,7 +209,10 @@ public function persistEntity(EntityManagerInterface $entityManager, $entityInst $entityManager->flush(); } - protected function getChainOptions() + /** + * @return int[]|string[] + */ + protected function getChainOptions(): array { $options = []; foreach (array_keys($this->chainProcessorManager->getRewDefinitions()) as $definitionName) { diff --git a/DependencyInjection/Oliverde8PhpEtlEasyAdminExtension.php b/DependencyInjection/Oliverde8PhpEtlEasyAdminExtension.php index 9451b91..ca919b7 100644 --- a/DependencyInjection/Oliverde8PhpEtlEasyAdminExtension.php +++ b/DependencyInjection/Oliverde8PhpEtlEasyAdminExtension.php @@ -1,5 +1,6 @@ load('services.yml'); diff --git a/Oliverde8PhpEtlEasyAdminBundle.php b/Oliverde8PhpEtlEasyAdminBundle.php index 5e9a5a8..f096c01 100644 --- a/Oliverde8PhpEtlEasyAdminBundle.php +++ b/Oliverde8PhpEtlEasyAdminBundle.php @@ -1,5 +1,7 @@ Date: Tue, 21 Apr 2026 10:01:14 +0200 Subject: [PATCH 3/4] chore(phpstan): fix code with phpstan --- Controller/Admin/EtlExecutionCrudController.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Controller/Admin/EtlExecutionCrudController.php b/Controller/Admin/EtlExecutionCrudController.php index 02e32ef..263e864 100644 --- a/Controller/Admin/EtlExecutionCrudController.php +++ b/Controller/Admin/EtlExecutionCrudController.php @@ -68,22 +68,22 @@ public function configureFields(string $pageName): iterable { if (Crud::PAGE_DETAIL === $pageName) { return [ - FormField::addPanel("Details")->addCssClass("col-12 col-xl-6"), + FormField::addFieldset("Details")->addCssClass("col-12 col-xl-6"), Field::new('name'), Field::new('username'), TextField::new('status')->setTemplatePath('@Oliverde8PhpEtlEasyAdmin/fields/status.html.twig'), - FormField::addPanel()->addCssClass("col-12 col-xl-6"), + FormField::addFieldset()->addCssClass("col-12 col-xl-6"), Field::new('createTime'), Field::new('startTime'), Field::new('endTime'), Field::new('failTime'), - FormField::addPanel('Execution Inputs')->addCssClass('col-12'), + FormField::addFieldset('Execution Inputs')->addCssClass('col-12'), CodeEditorField::new('inputData')->setTemplatePath('@Oliverde8PhpEtlEasyAdmin/fields/code_editor.html.twig')->addCssClass('etl-json-div'), CodeEditorField::new('inputOptions')->setTemplatePath('@Oliverde8PhpEtlEasyAdmin/fields/code_editor.html.twig')->addCssClass('etl-json-div'), CodeEditorField::new('definition')->setTemplatePath('@Oliverde8PhpEtlEasyAdmin/fields/code_editor.html.twig'), - FormField::addPanel('Execution outpus')->addCssClass("col-12"), + FormField::addFieldset('Execution outpus')->addCssClass("col-12"), TextField::new('Files')->formatValue(function ($value, EtlExecution $entity): array { $urls = []; if ($this->isGranted(EtlExecutionVoter::DOWNLOAD, EtlExecution::class)) { @@ -195,7 +195,7 @@ public function createEntity(string $entityFqcn): object $user = $this->getUser(); $username = null; if ($user instanceof \Symfony\Component\Security\Core\User\UserInterface) { - $username = $user->getUsername(); + $username = $user->getUserIdentifier(); } $execution = new EtlExecution("", "", [], []); @@ -215,7 +215,7 @@ public function persistEntity(EntityManagerInterface $entityManager, $entityInst protected function getChainOptions(): array { $options = []; - foreach (array_keys($this->chainProcessorManager->getRewDefinitions()) as $definitionName) { + foreach (array_keys($this->chainProcessorManager->getRawDefinitions()) as $definitionName) { $options[$definitionName] = $definitionName; } From 2cd32cacf7c066bc679cffb3d0c74801b8fc2947 Mon Sep 17 00:00:00 2001 From: johbuch Date: Tue, 21 Apr 2026 10:06:03 +0200 Subject: [PATCH 4/4] chore(phpcs): fix code with phpcs --- Controller/Admin/EtlDashboardController.php | 30 +++++------ .../Admin/EtlDownloadFileController.php | 14 ++--- .../Admin/EtlExecutionCrudController.php | 53 ++++++++++--------- .../Oliverde8PhpEtlEasyAdminExtension.php | 4 -- Oliverde8PhpEtlEasyAdminBundle.php | 5 -- 5 files changed, 49 insertions(+), 57 deletions(-) diff --git a/Controller/Admin/EtlDashboardController.php b/Controller/Admin/EtlDashboardController.php index 68de9d2..c86834d 100644 --- a/Controller/Admin/EtlDashboardController.php +++ b/Controller/Admin/EtlDashboardController.php @@ -1,5 +1,7 @@ denyAccessUnlessGranted(EtlExecutionVoter::DASHBOARD, EtlExecution::class); - if (is_null($endDate)) { + if (null === $endDate) { $endDate = new \DateTime(); } - if (is_null($startDate)) { + if (null === $startDate) { $startDate = new \DateTime('7 days ago'); } - //TODO add Acl here for future proofing. + // TODO add Acl here for future proofing. return $this->render( - "@Oliverde8PhpEtlEasyAdmin/admin/dashboard.html.twig", + '@Oliverde8PhpEtlEasyAdmin/admin/dashboard.html.twig', [ - 'num_waiting' => $this->etlExecutionRepository->getCountInStatus($startDate, $endDate, EtlExecution::STATUS_WAITING), - 'num_running' => $this->etlExecutionRepository->getCountInStatus($startDate, $endDate, EtlExecution::STATUS_RUNNING), - 'num_success' => $this->etlExecutionRepository->getCountInStatus($startDate, $endDate, EtlExecution::STATUS_SUCCESS), - 'num_failure' => $this->etlExecutionRepository->getCountInStatus($startDate, $endDate, EtlExecution::STATUS_FAILURE), - 'max_wait_time' => $this->etlExecutionRepository->getMaxWaitTime($startDate, $endDate), - 'avg_wait_time' => $this->etlExecutionRepository->getAvgWaitTime($startDate, $endDate), - 'most_executed' => $this->etlExecutionRepository->getMostExecutedJobs($startDate, $endDate, 10), + 'num_waiting' => $this->etlExecutionRepository->getCountInStatus($startDate, $endDate, EtlExecution::STATUS_WAITING), + 'num_running' => $this->etlExecutionRepository->getCountInStatus($startDate, $endDate, EtlExecution::STATUS_RUNNING), + 'num_success' => $this->etlExecutionRepository->getCountInStatus($startDate, $endDate, EtlExecution::STATUS_SUCCESS), + 'num_failure' => $this->etlExecutionRepository->getCountInStatus($startDate, $endDate, EtlExecution::STATUS_FAILURE), + 'max_wait_time' => $this->etlExecutionRepository->getMaxWaitTime($startDate, $endDate), + 'avg_wait_time' => $this->etlExecutionRepository->getAvgWaitTime($startDate, $endDate), + 'most_executed' => $this->etlExecutionRepository->getMostExecutedJobs($startDate, $endDate, 10), 'most_time_spent' => $this->etlExecutionRepository->getMostTimeSpentJobs($startDate, $endDate, 10), - 'longest' => $this->etlExecutionRepository->getLongestJobs($startDate, $endDate, 10), - 'crudController' => EtlExecutionCrudController::class + 'longest' => $this->etlExecutionRepository->getLongestJobs($startDate, $endDate, 10), + 'crudController' => EtlExecutionCrudController::class, ] ); } diff --git a/Controller/Admin/EtlDownloadFileController.php b/Controller/Admin/EtlDownloadFileController.php index 25cbfc6..ed32d75 100644 --- a/Controller/Admin/EtlDownloadFileController.php +++ b/Controller/Admin/EtlDownloadFileController.php @@ -1,28 +1,28 @@ denyAccessUnlessGranted(EtlExecutionVoter::DOWNLOAD, EtlExecution::class); $context = $this->executionContextFactory->get(['etl' => ['execution' => $execution]]); - $file = $context->getFileSystem()->readStream($filename); + $file = $context->getFileSystem()->readStream($filename); - $response = new StreamedResponse(function () use ($file): void { - $outputStream = fopen('php://output', 'wb'); + $response = new StreamedResponse(static function () use ($file): void { + $outputStream = fopen('php://output', 'w'); stream_copy_to_stream($file, $outputStream); }); $disposition = HeaderUtils::makeDisposition( HeaderUtils::DISPOSITION_ATTACHMENT, - sprintf('execution-%s-%s-', $execution->getName(), $execution->getId()) . $filename + \sprintf('execution-%s-%s-', $execution->getName(), $execution->getId()).$filename ); $response->headers->set('Content-Disposition', $disposition); diff --git a/Controller/Admin/EtlExecutionCrudController.php b/Controller/Admin/EtlExecutionCrudController.php index 263e864..3128518 100644 --- a/Controller/Admin/EtlExecutionCrudController.php +++ b/Controller/Admin/EtlExecutionCrudController.php @@ -1,5 +1,7 @@ setPageTitle("index", "Etl Executions") + ->setPageTitle('index', 'Etl Executions') ->setDateTimeFormat('dd/MM/y - HH:mm:ss') - ->setSearchFields(["name", "id"]) + ->setSearchFields(['name', 'id']) ->setDefaultSort(['id' => 'DESC']); } @@ -68,11 +68,11 @@ public function configureFields(string $pageName): iterable { if (Crud::PAGE_DETAIL === $pageName) { return [ - FormField::addFieldset("Details")->addCssClass("col-12 col-xl-6"), + FormField::addFieldset('Details')->addCssClass('col-12 col-xl-6'), Field::new('name'), Field::new('username'), TextField::new('status')->setTemplatePath('@Oliverde8PhpEtlEasyAdmin/fields/status.html.twig'), - FormField::addFieldset()->addCssClass("col-12 col-xl-6"), + FormField::addFieldset()->addCssClass('col-12 col-xl-6'), Field::new('createTime'), Field::new('startTime'), Field::new('endTime'), @@ -83,17 +83,17 @@ public function configureFields(string $pageName): iterable CodeEditorField::new('inputOptions')->setTemplatePath('@Oliverde8PhpEtlEasyAdmin/fields/code_editor.html.twig')->addCssClass('etl-json-div'), CodeEditorField::new('definition')->setTemplatePath('@Oliverde8PhpEtlEasyAdmin/fields/code_editor.html.twig'), - FormField::addFieldset('Execution outpus')->addCssClass("col-12"), + FormField::addFieldset('Execution outpus')->addCssClass('col-12'), TextField::new('Files')->formatValue(function ($value, EtlExecution $entity): array { $urls = []; if ($this->isGranted(EtlExecutionVoter::DOWNLOAD, EtlExecution::class)) { $context = $this->executionContextFactory->get(['etl' => ['execution' => $entity]]); - $files = $context->getFileSystem()->listContents("/"); + $files = $context->getFileSystem()->listContents('/'); foreach ($files as $file) { if (!str_starts_with($file, '.')) { $url = $this->adminUrlGenerator - ->setRoute("etl_execution_download_file", ['execution' => $entity->getId(), 'filename' => $file]) + ->setRoute('etl_execution_download_file', ['execution' => $entity->getId(), 'filename' => $file]) ->generateUrl(); $urls[$url] = $file; @@ -107,34 +107,34 @@ public function configureFields(string $pageName): iterable CodeEditorField::new('errorMessage')->setTemplatePath('@Oliverde8PhpEtlEasyAdmin/fields/code_editor.html.twig'), TextField::new('Logs')->formatValue(function ($value, EtlExecution $entity): array { $context = $this->executionContextFactory->get(['etl' => ['execution' => $entity]]); - $logs = []; - if ($context->getFileSystem()->fileExists("execution.log")) { - $file = $context->getFileSystem()->readStream("execution.log"); - $i = 0; + $logs = []; + if ($context->getFileSystem()->fileExists('execution.log')) { + $file = $context->getFileSystem()->readStream('execution.log'); + $i = 0; while ($i < 100 && $line = fgets($file)) { $logs[] = $line; - $i++; + ++$i; } fclose($file); } - $url = ""; + $url = ''; $moreLogs = false; - if ($logs !== []) { + if ([] !== $logs) { $url = $this->adminUrlGenerator - ->setRoute("etl_execution_download_file", ['execution' => $entity->getId(), 'filename' => 'execution.log']) + ->setRoute('etl_execution_download_file', ['execution' => $entity->getId(), 'filename' => 'execution.log']) ->generateUrl(); } - if (count($logs) > 100) { + if (\count($logs) > 100) { $moreLogs = true; } return [ - "lines" => $logs, + 'lines' => $logs, 'downloadUrl' => $url, - 'moreLogs' => $moreLogs, + 'moreLogs' => $moreLogs, ]; })->setTemplatePath('@Oliverde8PhpEtlEasyAdmin/fields/logs.html.twig'), @@ -157,8 +157,8 @@ public function configureFields(string $pageName): iterable return [ ChoiceField::new('name', 'Chain Name') ->setChoices($this->getChainOptions()), - CodeEditorField::new('inputData')->setCssClass("etl-json-input"), - CodeEditorField::new('inputOptions')->setCssClass("etl-json-input"), + CodeEditorField::new('inputData')->setCssClass('etl-json-input'), + CodeEditorField::new('inputOptions')->setCssClass('etl-json-input'), ]; } @@ -192,14 +192,15 @@ public function configureFilters(Filters $filters): Filters public function createEntity(string $entityFqcn): object { - $user = $this->getUser(); + $user = $this->getUser(); $username = null; if ($user instanceof \Symfony\Component\Security\Core\User\UserInterface) { $username = $user->getUserIdentifier(); } - $execution = new EtlExecution("", "", [], []); + $execution = new EtlExecution('', '', [], []); $execution->setUsername($username); + return $execution; } diff --git a/DependencyInjection/Oliverde8PhpEtlEasyAdminExtension.php b/DependencyInjection/Oliverde8PhpEtlEasyAdminExtension.php index ca919b7..d778473 100644 --- a/DependencyInjection/Oliverde8PhpEtlEasyAdminExtension.php +++ b/DependencyInjection/Oliverde8PhpEtlEasyAdminExtension.php @@ -4,7 +4,6 @@ namespace Oliverde8\PhpEtlEasyAdminBundle\DependencyInjection; - use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\Extension; @@ -12,9 +11,6 @@ class Oliverde8PhpEtlEasyAdminExtension extends Extension { - /** - * @inheritDoc - */ public function load(array $configs, ContainerBuilder $container): void { $loader = new YamlFileLoader($container, new FileLocator(\dirname(__DIR__).'/Resources/config')); diff --git a/Oliverde8PhpEtlEasyAdminBundle.php b/Oliverde8PhpEtlEasyAdminBundle.php index f096c01..0bc28ca 100644 --- a/Oliverde8PhpEtlEasyAdminBundle.php +++ b/Oliverde8PhpEtlEasyAdminBundle.php @@ -4,11 +4,6 @@ namespace Oliverde8\PhpEtlEasyAdminBundle; -use Oliverde8\PhpEtlBundle\DependencyInjection\Compiler\ChainBuilderCompiler; -use Oliverde8\PhpEtlBundle\DependencyInjection\Compiler\ChainCompiler; -use Oliverde8\PhpEtlBundle\DependencyInjection\Compiler\ChainParameterCompiler; -use Oliverde8\PhpEtlBundle\DependencyInjection\Compiler\RuleEngineCompiler; -use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; class Oliverde8PhpEtlEasyAdminBundle extends Bundle