2

My goal

In Plesk i want to run a PHP script frequently using PHP 7.2. It has to be as a PHP script and not console command (see "my environment" for more details). My current Symfony 4.2 based implementation works fine, but it is marked deprecated.

As stated here, the ContainerAwareCommand is marked deprecated in Symfony 4.2. Unfortunately, the referenced article about how to solve this issue in the future doesn't contain information about it.

My environment

My shared webhosting (Plesk) runs with PHP 7.0 but allows scripts to run with PHP 7.2. Later is only possible, if it directly runs the PHP script and not as a console command. I require PHP 7.2.

I know the injection types in Symfony. Based on my current knowledge, this issue only can be solved by using the getContainer approach or providing all services by hand, for instance via constructor, which would result in a code mess.

Current solution

File: cron1.php

<?php

// namespaces, Dotenv and gathering $env and $debug
// ... 

$kernel = new Kernel($env, $debug);

$app = new Application($kernel);
$app->add(new FillCronjobQueueCommand());
$app->setDefaultCommand('fill_cronjob_queue');
$app->run();

File: FillCronjobQueueCommand.php

<?php 

// ...

use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class FillCronjobQueueCommand extends ContainerAwareCommand
{
    protected function configure()
    {
        $this->setName('fill_cronjob_queue');
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        // using "$this->getContainer()" is deprecated since Symfony 4.2 
        $manager = $this->getContainer()->get('doctrine')->getManager();

        $cron_queue_repo = $manager->getRepository(CronjobQueue::class);

        $cronjobs = $manager->getRepository(Cronjob::class)->findAll();

        $logger = $this->getContainer()->get('logger');

        // ...
    }
}

k00ni
  • 315
  • 4
  • 17
  • Your notes about console vs script are confusing. bin/console is a php script. I see no reason why you could not run something like "/usr/bin/php7.2 bin/console fill_cronjob_queue". The startup code you posted using Application is really for when you want to use the console component outside of the framework. – Cerad May 02 '19 at 13:43
  • I currently had no time to find out, how to run a script in Plesk using PHP 7.2 via console. Maybe its that easy. My approach instead was to implement a clean application using Symfony. With your suggestion below, i can work for now without any deprecation warnings. After my hoster upgrades, this approach can be replaced by the answer posted below. – k00ni May 02 '19 at 13:55
  • If you say so. But if you look at bin/console then you can see that there is basically no difference between your app's code and the code in console. If you know how to run your script in 7.2 then you already know how to run bin/console with 7.2. – Cerad May 02 '19 at 13:59

1 Answers1

8

Answer for now

For my case it seems copying the ContainerAwareCommand class is the best way, as long as not stated otherwise (Thanks "Cerad"). This allows me to keep the current functionality and get rid of deprecated warnings. For me its also temporary solution (until Hoster upgrades to PHP 7.2) and has therefore no impact for future major upgrades of Symfony.

Nevertheless, i recommend the answer below and will implement it in the future.


Recommended answer

According to this blog post on the symfony website Deprecated ContainerAwareCommand

The alternative is to extend commands from the Command class and use proper service injection in the command constructor

So the correct way is :

<?php 

// ...

use Symfony\Component\Console\Command\Command
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\ORM\EntityManagerInterface;
use PSR\Log\LoggerInterface;

class FillCronjobQueueCommand extends Command
{
    public function __construct(EntityManagerInterface $manager, LoggerInterface $logger)
    {
        $this->manager = $manager;
        $this->logger = $logger;
    }

    protected function configure()
    {
        $this->setName('fill_cronjob_queue');
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $cron_queue_repo = $this->manager->getRepository(CronjobQueue::class);

        $cronjobs = $this->manager->getRepository(Cronjob::class)->findAll();

        // ...
    }
}
k00ni
  • 315
  • 4
  • 17
stephan.mada
  • 1,110
  • 9
  • 11
  • and if you want a service from the container you can inject them normally via `Application` – Flug May 02 '19 at 13:22
  • 1
    At the risk of nit-picking, it is important to call parent::_construct for things to work. It will be interesting to see how the op responds as they have already rejected this approach. – Cerad May 02 '19 at 13:23
  • 1
    Thanks for your fast answer. As i explained above, i would have a hard time when using injection via constructor. In my scenario i have a file which sets up the application and runs it. Providing services via constructor requires me to setup services by myself. I don't want to do that by hand, instead Symfony has to handle it. But i don't know how. – k00ni May 02 '19 at 13:25
  • I think you should follow symfony best practice and take time to inject all the services you need. However, you can simply inject the container and get all the services you need from it. __construct(\Psr\Container\ContainerInterface) { $this->container = $container } . Then you can easily get other services like : $manager = $this->container->get('doctrine')->getManager(); – stephan.mada May 02 '19 at 13:31
  • 1
    @k00ni You need to make your command itself a service so Symfony will take care of injecting the dependencies. But based on your posted code and your "console command" vs "script" notes, I don't really see what you are trying to do. But if this is a real big deal for you then just look in the framework bundle containerawarecommand code and copy/paste it into your command. – Cerad May 02 '19 at 13:32
  • @Cerad: Your suggestion takes the cake, copying the ContainerAwareCommand code and use it. Never thought about it :) This solution smells, i know, but because its only temporary (until my Hoster upgrades to PHP 7.2) and not incompatible to future code, i'll keep it for now. Nevertheless, using Symfony best practices is my top priority. Thank you all for the quick responses! – k00ni May 02 '19 at 13:53
  • I edited the current answer and added my current solution. This way people with a similar problem can decide for themselves. Also marked post as answer. – k00ni May 02 '19 at 14:04
  • Better inject repository as service. `$this->manager` is just another service locator here. – Tomas Votruba May 02 '19 at 15:41