Artisan::call() outside the Laravel framework
Asked Answered
L

3

7

I want to create a cron job for Laravel 5.2

My shared host (on OVH), only allows me to point to the full path of a file, and I am not able to use the recommended Cron entry from Laravel's docs, ie :

* * * * * php /path/to/artisan schedule:run >> /dev/null 2>&1

Therefore, I have to call the Artisan command from a .php file, outside of the Laravel framework.

Here is what my public/cron.php file looks like so far:

<?php

require __DIR__.'/../bootstrap/autoload.php';

use Illuminate\Support\Facades\Artisan;

Artisan::call('refresh');

refresh being my command for regenerating thumbnails inside my app.

When accessing cron.php through my browser (testing on local XAMPP), the following error occurs:

Fatal error: Uncaught RuntimeException: A facade root has not been set. in 
C:\xampp\htdocs\site\vendor\laravel\framework\src\Illuminate\Support\Facades\Facade.php:210

Stack trace: 
#0 C:\xampp\htdocs\site\public\cron.php(7): Illuminate\Support\Facades\Facade::__callStatic('call', Array) 
#1 {main} thrown in C:\xampp\htdocs\site\vendor\laravel\framework\src\Illuminate\Support\Facades\Facade.php on line 210

I have also tried to boot the app, but it doesn't make any differences

$app = require_once __DIR__.'/../bootstrap/app.php';
$app->boot();

To avoid using the Artisan Facade, I tried calling the underlying Kernel Class directly:

use Illuminate\Contracts\Console\Kernel;

$kernel = new Kernel;
$kernel->call('refresh');

But this returns:

Uncaught Error: Cannot instantiate interface Illuminate\Contracts\Console\Kernel

EDIT: Here is a screenshot of OVH cron interface. The cron task is customized by OVH and only allows to point to the fullpath uri of a file - which file would execute my artisan command-. My question is, what should I put in this file, and should it be a PHP file, or a CMD?

OVH cron interface

Longlimbed answered 5/5, 2016 at 23:14 Comment(7)
So you can't edit your crontab with 'crontab -e'? How will you execute your .file periodically if you cannot create cronjobs?Ossifrage
@Ossifrage OP said he owns a shared hosting at OVH. OVH lets customers create cron jobs in their Manager. crontab -e is just a shortcut to open /var/spool/cron/crontabs/<username> with favorite text editor. There are many other ways to put some contents in that file. Obviously, a script can handle it too.Tuck
I updated the questionLonglimbed
I see on your other question that you are on OVH Pro. This hosting normally comes with SSH access: check your manager... User is the same as for FTP and password should the same too. Via SSH, you can use crontab -e to run artisan command of you choice using standard syntax and following Laravel documentation.Tuck
@Tuck I am not comfortable enough with SSH yet, I am still a FTP newbie user, but I'll start learning very soon. For now, your other answer is very satisfying, thx!Longlimbed
@Tuck By the way, do you have any idea on what is going wrong on my other question with the session files? I use multisites on OVH, and they are placed at the route of my OVH host, and not in the www folder (for security, because the www folder is publicly available from mynickname.cluster005.ovh.net and I dont want my sites to be available from www/site1, www/site2...). So I was thinking that maybe only the files inside www folder are writable, and maybe not the one outside (at the root)?Longlimbed
I've done some search about your other question, but not much for now, no obvious idea came to me. I will have a look, but maybe I'll need some more details... See you on your other question ;)Tuck
T
13

What you want to do is run a specific Artisan command from within a script.

You can achieve this by copying artisan.php and forcing the input to what you want:

#!/usr/bin/env php
<?php

require __DIR__.'/bootstrap/autoload.php';

$app = require_once __DIR__.'/bootstrap/app.php';

$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);

$status = $kernel->handle(
    $input = new Symfony\Component\Console\Input\ArrayInput(['command' => 'refresh']),
    new Symfony\Component\Console\Output\ConsoleOutput
);

$kernel->terminate($input, $status);

exit($status);

If you compare this script to artisan.php, you'll see that I've just forced the input passed to $kernel->handle() method. It does not read the input from the CLI anymore, it takes these arguments, as an array. See Symfony Console Component documentation for more details.

If you need to pass arguments to your script, just set the input accordingly:

$input = new Symfony\Component\Console\Input\ArrayInput([
    'command' => 'refresh',
    'arg_foo' => 'foo',
    '--option_bar' => 42
]);

$status = $kernel->handle(
    $input,
    new Symfony\Component\Console\Output\ConsoleOutput
);

Now, you can put this script where you want, it does not need to be accessible through web via a browser (it should not, by the way).

If you put it at the root of your hosting at OVH, I mean NOT in www, you just have to fill the form very simply:

OVH Shared Hosting Cronjob - Step 1

If you want your script to be accessible through the web (which is not recommanded for obvious security reasons, but still), place it in your www directory, change the paths to bootstrap/autoload.php and bootstrap/app.php and give your script a name that is not easy to guess.

In the form in the OVH manager, don't forget to add www/ at the beginning of the path of the script.

There is no need to specify php script_name, since the manager handles it for you, when you choose PHP version. Just type the path of the script PHP will execute.

Tuck answered 8/5, 2016 at 23:56 Comment(7)
Merci ! That's exactly what I was looking for. Very clear step by step explanation, everything works perfectly.Longlimbed
I have to wait 3 more hours before I can deliver the bounty, but you'll definitely get it ;)Longlimbed
Thanks ! Glad it helped !Tuck
In your first snipper you have this line $kernel->terminate($input, $status);, can you please explain me where does the $input come from?Filmy
My mistake. It's old now, and I don't have the code anymore... I think I just forgot to copy correctly the $input declaration in the $status declaration (yeah, declaration inside another declaration, as in the original artisan file). I've fixed the snippet.Tuck
This answer helped me a lot, I'm in the same scenario but how to get logs for this ? I have an error status code of 255.People
The error code is returned by your command. Symfony/Console does not create it for youTuck
P
2

Just try simple:

shell_exec('php artisan refresh');

If it doesn't work, try to add appropriate paths to both php and artisan.

Pellegrino answered 6/5, 2016 at 1:53 Comment(1)
This works perfectly on my localhost, but when I try on production, it 'breaks' the website and I then I get random 500 errors appearing when browsing. Here are the logs from the host: FastCGI: comm with server "/home/xxx.com/public/cron-refresh.php" aborted: idle timeout (300 sec) FastCGI: incomplete headers (0 bytes) received from server "/home/xxx.com/public/cron-refresh.php" FastCGI: An error happend on Fastcgi processing, fallback to CGILonglimbed
O
1

If you just want set a cron job. please edit crontab and use "your/app/path/php artisan cron:job" to excute your command directly.

Oculist answered 6/5, 2016 at 1:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.