I know there's questions about this but so far none has helped me solve my problem.
I have a PHP script whose job is to send scheduled e-mails. It does this by calling a web service, which I control, via cURL.
Run in the browser, it works fine. Run via CRON, the cURL response is empty. It never reaches the web service; I know this because I had the WS write a text file when it's contacted. It does if you access via browser, not via CRON.
I know CRON runs in a different environment and I'm not relying on any environmental vars e.g. $_SERVER
. The path to require()
isn't the problem, either, as it's successfully getting data out of that file to connect to the DB.
This question suggested adding the following cURL opts, which I've done, but no dice:
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
Here's my PHP script:
//prep
define('WS_URL', 'http://mywebservicedomain.com/index.php/web_service');
require '../application/config/database.php';
$db = new mysqli($db['default']['hostname'], $db['default']['username'], $db['default']['password'], $db['default']['database']) or die('failed to connect to DB');
//get scheduled e-mails
$res = $db->query('SELECT * FROM _cron_emails WHERE send_when < NOW()');
//process each...
while ($email_arr = $res->fetch_assoc()) {
//...get API connection info
$api_accnt = $db->query($sql = 'SELECT id, secret FROM _api_accounts WHERE project = "'.$email_arr['project'].'"');
$api_accnt = $api_accnt->fetch_assoc();
//...recreate $_POST env
$tmp = json_decode($email_arr['post_vars'], 1);
$_POST = array();
foreach($tmp as $key => $val) $_POST[$key] = !is_array($val) ? $val : implode(',', $val);
//...call API to send e-mail
$ch = curl_init($url = WS_URL.'/send_scheduled_email/'.$email_arr['email_id'].'/'.$email_arr['store_id'].'/'.$email_arr['item_id']);
curl_setopt_array($ch, array(
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_SAFE_UPLOAD => true,
CURLOPT_SSL_VERIFYHOST => 1, //<-- tried adding this
CURLOPT_SSL_VERIFYPEER => 1, //<-- ditto
CURLOPT_POSTFIELDS => array_merge($_POST, array(
'api_key' => $api_accnt['id'],
'api_secret' => $api_accnt['secret'],
))
));
$resp = curl_exec($ch); //<-- empty when CRON
//if e-mail was sent OK, or decider script said it was ineligible, delete it from the queue
if (($resp == 'ok' || $resp == 'ineligible'))
$db->query('DELETE FROM _cron_emails WHERE id = '.$email_arr['id'].' LIMIT 1');
}
Does anyone have any idea why it fails to connect to my web service via cURL only when run in CRON?
Here's the CRON job:
/usr/bin/php /home/desyn/public_html/cron/send_scheduled_emails.php
In the end this turned out to be a very particular, and I imagine uncommon, problem. I doubt it'll prove too much help to others, but I leave it here in case.
Essentially, my server didn't like cURL'ing itself, via a domain.
The server being called by cURL in the CRON script was the same server that the CRON task was running on (but a different account and website.)
This has not proved to be a problem via a browser; only via CRON.
Changing the cURL request to use the server's local IP, rather than using the domain, solved this.