Laravel scheduling: How to run the scheduler with proc_open disabled?

We are working on a Laravel 8.x project, developed it locally with Sail (Docker dev environment) and wanted it to release on production. All OK..

Until we reached the task schedulers, where the party started. To prevent other from debugging, searching and coming to the conclusion that the PHP function proc_open is disabled on there shared host.

To prevent other from the same issues, we want to share the simple but stable solution without re-writing existing commands and reuse existing logic.

What is a Laravel Command?

For our project we have created several commands which accept input arguments. This works ideal if you want to verify a command is functional. As examples we are re-using example code from laravel.com.

if you are already familiar with Laravel Commands, you can skip this part

With below command we will make from the command line with artisan a new command called SendEmails

php artisan make:command SendEmails

A new file is created on app/Console/Commands/SendEmails.php, open this file and update the $signature, $description and handle() function with below content

As last step we can run the command, to verify it is functional. Above logic prints out the input argument of user, in this case testUser. With these steps you have created a command with one argument.

php artisan mail:send testUser

How to configure a command within the Laravel scheduler

When you have created some commands with arguments, the goal is to run them with the Laravel scheduler.
Open the file app/Console/Kernel.php and update both $commands and schedule() function like below example. After updating the Kernel.php, run schedule:run to verify its functional.

php artisan schedule:run

After running the command the output is placed in the file /storage/logs/schedule.log, the content is string(8) "testUser" as we print out the string 'testUser'.

The issue

Next step is to configure this on a host, in our case a shared host. We configured one cronjob in DirectAdmin and waited..

* * * * * cd /path-to-your-project && php artisan schedule:run 2>&1
        

In our case above command was not functional, so we modified it a little bit to match it with the shared hosting.

php /home/user/domains/domain.com/public_html/artisan schedule:run
php /home/[user name]/domains/[domain]/public_html/artisan schedule:run
        

If you do not now the absolute path to artisan, executed the command pwd, to get the absolute location and use FTP to build up a absolute path to artisan.

After running for a few minutes we checked the log file logs/schedule.log

[2021-02-02 13:23:04] local.ERROR: The Process class relies on proc_open, 
which is not available on your PHP installation. {"exception":"[object] 
(Symfony\\Component\\Process\\Exception\\LogicException(code: 0): The Process 
class relies on proc_open, which is not available on your PHP installation. 
at /home/user/domains/domain.com/public_html/vendor/symfony/process/
Process.php:144)
        

Hmmm.. what happend? Im just running a Laravel project with only PHP files, why does it need proc_open?

Good question; Laravel, or actually a library symfony process uses proc_open to execute the command. This means that with the Laravel scheduler you can run any command, from pwd, mkdir or php artisan make:command SendEmails. It calls proc_open and any command will be executed.

We've found below from laracasts.com discussion:

Yes it uses proc_open, because it's using the symfony/process package in the 
background. However as long the users can't pass any input to your commands 
you should be good to go!
source:https://laracasts.com/discuss/channels/laravel/does-laravel-task-scheduling-need-to-proc-open-and-proc-close
        

How to check if proc_open is disabled?

To check if proc_open is disabled you can check it within the php.ini file. On most shared hosts the end-user does not has access to this file. As alternative you can run phpinfo(); in a php script. It will generate a overview of the PHP's configuration.
Search for disable_functions and verify if proc_open is listed.

The Pre-Solution

Before we start talking about the solution, we use the Clean Architecture and interactors models, blog 1 / blog 2.

The interactor is an object which receives inputs from user, gets work done 
by entities and returns the output to the user. The interactor sets things 
in motion like an orchestra director to make the execution of a use case 
possible. There is exactly one interactor per use case in the system.
        

So simplified an interactor is an object (abstraction layer) which is simple to call and contains one purpose.

With this concept of interactors, we keep the Laravel command clear from any business logic.

As we could not use the default scheduler we have created our own cronjob commands. They are located in a subfolder of the commands which result in the following location app/Console/Commands/cron/SendEmailsCron.php

With Laravel DI (dependency injection) and the use of interactors our SendEmailsCron.php is simple, flexible and functional. With the command cron:sendEmail the cronjob can be called by artisan

            php artisan make:command cron/SendEmailsCron
        

Configure the custom cronjobs

The last step is to configure this cronjob on the shared host, in DirectAdmin for example.

Any output will be pushed to the cronjob output, which can be send by email with DirectAdmin.

php /home/user/domains/domain.com/public_html/artisan cron:sendEmail
php /home/[user name]/domains/[domain]/public_html/artisan cron:sendEmail
        

Links

There you have it, we can skip the Laravel Scheduler and proc_open by running command with its own cronjob. If you have any questions or feedback, we like to hear it in the comments!

Thank you for reading this article, If you liked the article, help others find this article by sharing it.

Reacties

Populaire posts van deze blog

Android-x86 virtual machine configuration tips

Android and SonarQube with code coverage

Setup SonarQube with Docker