Cannot setup a cron job on Cpanel - php

Trying to add a cron job on Cpanel to run every 5 minutes. I added this to the command input (removed personal details from the url)
/usr/bin/php -q /home/my_name/public_html/staging/the_web_site/wp-content/themes/my_child_theme/functions.php updateproducts
Then on the functions.php, I have this:
if (!empty($argv[1])) {
switch ($argv[1]) {
case "updateproducts":
update_products();
break;
}
}
The update_products() function runs without any error manually if I trigger it with a button on admin page. But, no matter what I do on the cronjob tab, it doesn't run.
Any idea?
function update_products() {
global $wpdb;
$groups = get_groups_from_cron_jobs(100);
foreach ( $groups as $group ) {
$name = $group->group_name;
$sql = $wpdb->prepare("SELECT * FROM {$wpdb->prefix}products WHERE name='%s'", $name);
$products = $wpdb->get_results($sql);
if ( !empty($products) ){
$post_id = get_post_id_from_products($products);
//if there isn't a parent_id then create a new product
if ( !$post_id && $name != '' ) {
$post_id = create_a_new_product($name);
}
// make sure that all products will have now a parent_id
add_parent_id_on_products($name, $post_id);
insert_product_attributes($post_id, $products);
insert_product_variations($post_id, $products);
delete_group_from_cron_jobs($name);
}
}
}
Edit: Based on the answers/comments, I made extra research and found that I can load the $wpdb on any script. What I did is this:
if (!empty($argv[1])) {
switch ($argv[1]) {
case "updateproducts":
$path = $_SERVER['DOCUMENT_ROOT'];
require( $path . '/staging/the_web_site/wp-load.php' );
update_products();
break;
}
}
However, I still get the error on email:
Status: 500 Internal Server Error
X-Powered-By: PHP/5.6.31
Content-type: text/html; charset=UTF-8

I'm afraid there's some misunderstanding about how inter-operable the web environment and console environment are here.
In short, your function expects WordPress to exist. It expects that the user has accessed the function through the WordPress application, and that global variables such as $wpdb exist (among other things). If you access this function via the admin page, then this is all true, and the function works.
But if you access this function via the console (command line), none of this is true. WordPress does not exist within the context of that request. global $wpdb does not exist, because the function has not been accessed through WordPress, but through a direct command.
If you want this function to work on the command line, you will need to rewrite it to work within that environment. Which means no WordPress-specific helpers or functionalities at all.
Alternatively, you can use WordPress Crons, which are not true Cron jobs, but instead run whenever the site is accessed (within their defined time range). So if you have a cron that must run every five minutes, but you will not reliably have a new visitor every few minutes, these will not suit your needs, but they are an option.
http://www.wpbeginner.com/plugins/how-to-view-and-control-wordpress-cron-jobs/

Related

Request to launch background php script

I'm currently working on an internal website displaying a lot of statistics, and some pages or ajax scripts are extremely slow due to large datas.
What I'm searching is a way to launch theses scripts in background with a request, and then launch ajax requests to know the progress of the background script.
Is there any way to achieve this? I work with php7.0 and an apache2 server (I don't have direct access to apache server configuration, so if it's possible, I search for a client-side option)
If ever someone else is searching for a way to achieve this, here is the solution I found:
I call in Ajax a script, it forks itself and save the PID of the child process in the database.
Then I call session_write_close() in the child process to allow user making new requests, and the father process exits (not waiting for child end).
As the father exits, the user receive an answer to his request, and the child process continue his job.
Then in Ajax I call another script to get the evolution of the worker, and finally I get the result and kill the child process when everything is done.
Here is the code of my worker class:
class AsyncWorker
{
private $pid;
private $worker;
private $wMgr;
public function __construct($action, $content, $params = NULL)
{
$this->wMgr = new WorkersManager();
$pid = pcntl_fork(); // Process Fork
if ($pid < 0) {
Ajax::Response(AJX_ERR, "Impossible de fork le processus");
} else if ($pid == 0) { // In the child, we start the job and save the worker properties
sleep(1);
$this->pid = getmypid();
$this->worker = $this->wMgr->fetchBy(array("pid" => $this->pid));
if (!$this->worker) {
$this->worker = $this->wMgr->getEmptyObject();
$this->wMgr->create($this->worker);
}
$this->worker->setPid($this->pid);
$this->worker->setAction($action);
$this->worker->setContent($content);
$this->worker->setPercent(0.00);
$this->worker->setResult("");
$this->wMgr->update($this->worker);
$this->launch($params);
} else { // In the father, we save the pid to DB and answer the request.
$this->worker = $this->wMgr->fetchBy(array("pid" => $this->pid));
if (!$this->worker) {
$this->worker = $this->wMgr->getEmptyObject();
$this->worker->setPid($pid);
$this->wMgr->create($this->worker);
}
Ajax::Response(AJX_OK, "Worker started", $this->worker->getId());
}
}
// Worker job
private function launch($params = NULL)
{
global $form, $_PHPPATH, $url, $session;
session_write_close(); // This is useful to let the user make new requests
ob_start(); // Avoid writing anything
/*
** Some stuff specific to my app (include the worker files, etc..)
*/
$result = ob_get_contents(); // Get the wrote things and save them to DB as result
$this->worker->setResult($result);
$this->worker->setPercent(100);
ob_end_clean();
}
}
It's a bit tricky but I had no choices, as I have no access to server plugins and libraries.
you can make php script to execute shell bash script , or using exec() method for that

PHP CodeIgniter: How to run processes in the background without getting my whole website stuck

I have a website on an Ubuntu LAMP Server - that has a form which gets variables and then they get submitted to a function that handles them. The function calls other functions in the controller that "explodes" the variables, order them in an array and run a "for" loop on each variable, gets new data from slow APIs, and inserts the new data to the relevant tables in the database.
Whenever I submit a form, the whole website gets stuck (only for my IP, on other desktops the website continue working regularly), and I get redirected until I get to the requested "redirect("new/url);".
I have been researching this issue for a while and found this post as an example:
Continue PHP execution after sending HTTP response
After studding how this works in the server side, which is explained really good in this video: https://www.youtube.com/watch?v=xVSPv-9x3gk
I wanted to start learning how to write it's syntax and found out that this only work on CLI and not from APACHE, but I wasn't sure.
I opened this post a few days ago: PHP+fork(): How to run a fork in a PHP code
and after getting everything working from the server side, installing fork and figuring out the differences of the php.ini files in a server (I edited the apache2 php.ini, don't get mistaked), I stopped getting the errors I used to get for the "fork", but the processes don't run in the background, and I didn't get redirected.
This is the controller after adding fork:
<?php
// Registers a new keyword for prod to the DB.
public function add_keyword() {
$keyword_p = $this->input->post('key_word');
$prod = $this->input->post('prod_name');
$prod = $this->kas_model->search_prod_name($prod);
$prod = $prod[0]->prod_id;
$country = $this->input->post('key_country');
$keyword = explode(", ", $keyword_p);
var_dump($keyword);
$keyword_count = count($keyword);
echo "the keyword count: $keyword_count";
for ($i=0; $i < $keyword_count ; $i++) {
// create your next fork
$pid = pcntl_fork();
if(!$pid){
//*** get new vars from $keyword_count
//*** run API functions to get new data_arrays
//*** inserts new data for each $keyword_count to the DB
print "In child $i\n";
exit($i);
// end child
}
}
// we are the parent (main), check child's (optional)
while(pcntl_waitpid(0, $status) != -1){
$status = pcntl_wexitstatus($status);
echo "Child $status completed\n";
}
// your other main code: Redirect to main page.
redirect('banana/kas');
}
?>
And this is the controller without the fork:
// Registers a new keyword for prod to the DB.
public function add_keyword() {
$keyword_p = $this->input->post('key_word');
$prod = $this->input->post('prod_name');
$prod = $this->kas_model->search_prod_name($prod);
$prod = $prod[0]->prod_id;
$country = $this->input->post('key_country');
$keyword = explode(", ", $keyword_p);
var_dump($keyword);
$keyword_count = count($keyword);
echo "the keyword count: $keyword_count";
// problematic part that needs forking
for ($i=0; $i < $keyword_count ; $i++) {
// get new vars from $keyword_count
// run API functions to get new data_arrays
// inserts new data for each $keyword_count to the DB
}
// Redirect to main page.
redirect('banana/kas');
}
The for ($i=0; $i < $keyword_count ; $i++) { is the part that I want to get running in the background because it's taking too much time.
So now:
How can I get this working the way I explained? Because from what I see, fork isn't what I'm looking for, or I might be doing this wrong.
I will be happy to learn new techniques, so I will be happy to get suggestions about how I can do this in different ways. I am a self learner, and I found out the great advantages of Node.js for exmaple, which could have worked perfectly in this case if I would have learnt it. I will consider to learn working with Node.js in the future. sending server requests and getting back responses is awesome ;).
***** If there is a need to add more information about something, please tell me in comments and I will add more information to my post if you think it's relevant and I missed it.
What you're really after is a queue or a job system. There's one script running all the time, waiting for something to do. Once your original PHP script runs, it just adds a job to the list, and it can continue it's process as normal.
There's a few implementations of this - take a look at something like https://laravel.com/docs/5.1/queues

Wordpress cron jobs not firing

I have a wordpress website that allows a user to set up a reminder for a certain day, it stores it in a database where it can either be displayed when the user logs in, or send out an email to them.
I followed the example code given in http://codex.wordpress.org/Function_Reference/wp_cron and have placed the code below into the main .php file of a plugin I have written which functions perfectly in all other ways.
if ( ! wp_next_scheduled( 'checkReminderEmails' ) ) {
wp_schedule_event( 1411693200, 'daily', 'checkReminderEmails' );
} //1411693200 is the unix timestamp for 1am a couple of days ago
add_action( 'checkReminderEmails', 'sendReminderEmails' );
function sendReminderEmails()
{
$table_name = $wpdb->prefix."reminders";
$query = "SELECT * FROM $table_name WHERE sendReminder = 1 and reminderDate = CURRENT_DATE( )";
$appointments = $wpdb->get_results( $query);
foreach ( $appointments as $info )
{
$message = "Hello here is your reminder.";
$toAddress = $info->userEmail;
$subject = "This is your reminder.";
wp_mail( $toAddress, $subject, $message);
}
} // end sendReminderEmails()
I have checked the wp_options table in my PHP database and can see the following code there
{s:18:"sendReminderEmails";a:1:{s:32:"40cd750bba9870f18aada2478b24840a";a:3:{s:8:"schedule";s:5:"daily";s:4:"args";a:0:{}s:8:"interval";i:86400;}}}i:1411693200;a:1:
I can receive other emails from the website using wp_mail() so I know that the functionality is supported by the server, and I know that the wp_cron jobs aren't fired until the website is visited after the time has passed, but I have been unable to get this cron job to fire correctly. Am I missing something obvious?
Edit: For anybody wondering, I used the wp-cron-dashboard plugin (https://wordpress.org/plugins/wp-cron-dashboard/) despite the warning that it hasn't been updated in 2+ years to check that my cron job was scheduled properly.
I also had to add global $wpdb; to the top of my function, the reason it was failing to fire was because without declaring that I was unable to use the get_results() function.
I also discovered that if you go to wp-cron.php you will manually force any scheduled cron jobs to be fired, and all output will display there, so it is possible to debug a cron job by adding echo statements and going to that page.
I know this is old but one thing I see right away is you need global $wpdb; as the first line of the sendReminderEmails() function, otherwise it's going to bomb.
I often quickly test cron functions in my front-page.php by adding something like sendReminderEmails(); exit; to the top, just as a sanity check that the function works at all.

vBulletin robot only comes some of the time when called

I have been doing a lot of modification of my vBulletin forums, and I've taken particular interest in different forms of AI and botting on the forums. I recently created a plugin that will make the bot post in a thread if it is called. It works some of the time, and doesn't other times. I can't figure out why it's so unreliable.
It fires at the hook location "newpost_complete" and has the following code:
if (stristr($postinfo['pagetext'],'.robot:')){
preg_match('#^(?:.robot:)?([^:]+)#i',$postinfo['pagetext'], $matches);
$host = $matches[1];
require_once(DIR . '/includes/functions_robot.php');
run($host,$threadinfo['threadid']);
}
I'm not good with regex so I am not sure that preg_match is optimal. I've found that it rarely runs the code if you post .robot:hi: but if you quote a post with .robot:hi: in it, it will run without fail even if the actual quoted content is changed to something else.
Here's the relevant code in the functions_robot.php file:
function run($command,$threadid) {
global $vbulletin;
global $db;
if ($command == 'hi') {
$output = 'Hello.';
//Queries
}
}
Any ideas on what's causing it to be so unreliable? There's a lot of potential if I can get it running smoothly.
I was able to figure it out with the use of http://regex.larsolavtorvik.com/
I switched to postdata_presave hook instead of newpost_complete.
$pagetext =& $this->fetch_field('pagetext', 'post');
$threadid =& $this->fetch_field('threadid', 'post');
if (stristr($pagetext,'.robot:')){
preg_match('/(\.robot:)(.*)(:)/iU',$pagetext, $matches);
$host = $matches[2];
require_once(DIR . '/includes/functions_robot.php');
run($host,$threadid);
}
The new hook location meant it was usually firing off quicker than the insert on my actual post, making the robot post before me. I fixed this by adding usleep(500000); to the start of my run() function.

Drupal's profile_save_profile Doesn't Work in hook_cron, When Run by the Server's cron

I have a problem with the following implementation of hook_cron in Drupal 6.1.3.
The script below runs exactly as expected: it sends a welcome letter to new members, and updates a hidden field in their profile to designate that the letter has been sent. There are no errors in the letter, all new members are accounted for, etc.
The problem is that the last line -- updating the profile -- doesn't seem to work when Drupal cron is invoked by the 'real' cron on the server.
When I run cron manually (such as via /admin/reports/status/run-cron) the profile fields get updated as expected.
Any suggestions as to what might be causing this?
(Note, since someone will suggest it: members join by means outside of Drupal, and are uploaded to Drupal nightly, so Drupal's built-in welcome letters won't work (I think).)
<?php
function foo_cron() {
// Find users who have not received the new member letter,
// and send them a welcome email
// Get users who have not recd a message, as per the profile value setting
$pending_count_sql = "SELECT COUNT(*) FROM {profile_values} v
WHERE (v.value = 0) AND (v.fid = 7)"; //fid 7 is the profile field for profile_intro_email_sent
if (db_result(db_query($pending_count_sql))) {
// Load the message template, since we
// know we have users to feed into it.
$email_template_file = "/home/foo/public_html/drupal/" .
drupal_get_path('module', 'foo') .
"/emails/foo-new-member-email-template.txt";
$email_template_data = file_get_contents($email_template_file);
fclose($email_template_fh);
//We'll just pull the uid, since we have to run user_load anyway
$query = "SELECT v.uid FROM {profile_values} v
WHERE (v.value = 0) AND (v.fid = 7)";
$result = db_query(($query));
// Loop through the uids, loading profiles so as to access string replacement variables
while ($item = db_fetch_object($result)) {
$new_member = user_load($item->uid);
$translation_key = array(
// ... code that generates the string replacement array ...
);
// Compose the email component of the message, and send to user
$email_text = t($email_template_data, $translation_key);
$language = user_preferred_language($new_member); // use member's language preference
$params['subject'] = 'New Member Benefits - Welcome to FOO!';
$params['content-type'] = 'text/plain; charset=UTF-8; format=flowed;';
$params['content'] = $email_text;
drupal_mail('foo', 'welcome_letter', $new_member->mail, $language, $params, 'webmaster#foo.org');
// Mark the user's profile to indicate that the message was sent
$change = array(
// Rebuild all of the profile fields in this category,
// since they'll be deleted otherwise
'profile_first_name' => $new_member->profile_first_name,
'profile_last_name' => $new_member->profile_last_name,
'profile_intro_email_sent' => 1);
profile_save_profile($change, $new_member, "Membership Data");
}
}
}
To safely switch users
http://api.drupal.org/api/function/session_save_session/6
<?php
global $user;
$original_user = $user;
session_save_session(FALSE); // D7: use drupal_save_session(FALSE);
$user = user_load(array('uid' => 1)); // D7: use user_load(1);
// Take your action here where you pretend to be the user with UID = 1 (typically the admin user on a site)
// If your code fails, it's not a problem because the session will not be saved
$user = $original_user;
session_save_session(TRUE); // // D7: use drupal_save_session(TRUE);
// From here on the $user is back to normal so it's OK for the session to be saved
?>
Taken from here:
drupal dot org/node/218104
yes i confirm drupal cron user profile is "anonymous" so you have to add the permission de manager user for the "anonymous" user which is not very good in term of security ..
Not quite a random guess ... but close ...
When "real" cron runs code, it runs it as a particular user.
Similarly, when you run the Drupal cron code manually, the code will also be running as a particular user.
My suspicion is that the two users are different, with different permissions, and that's causing the failure.
Does the cron job's user have access to write the database, or read only?
Are there any log files generated by the cron job?
Update: By 'user' above, I'm referring to the user accounts on the host server, not Drupal accounts. For example, on a sandbox system I use for testing Drupal changes, Apache runs under the noone account.
profile_save_profile dosn't check for permissions or as far as I can see do 'global $user' so I don't know if this is the real issue.
$new_member = user_load($item->uid);
This looks wrong user_load should take an array not a uid (unless you are using Drupal7), but if that didn't work the rest of the code would fail too.

Categories