Syntax corrections in ajax/execute_background_services
[openemr.git] / library / ajax / execute_background_services.php
blobae98d82d38556233f0ec7d5f1bb2260af399cbc7
1 <?php
2 /**
3 * Manage background operations that should be executed at intervals.
5 * This script may be executed by a suitable Ajax request, by a cron job, or both.
7 * When called from cron, optinal args are [site] [service] [force]
8 * @param site to specify a specific site, 'default' used if omitted
9 * @param service to specify a specific service, 'all' used if omitted
10 * @param force '1' to ignore specified wait interval, '0' to honor wait interval
12 * The same parameters can be accessed via Ajax using the $_POST variables
13 * 'site', 'background_service', and 'background_force', respectively.
15 * For both calling methods, this script guarantees that each active
16 * background service function: (1) will not be called again before it has completed,
17 * and (2) will not be called any more frequently than at the specified interval
18 * (unless the force execution flag is used). A service function that is already running
19 * will not be called a second time even if the force execution flag is used.
21 * Notes for the default background behavior:
22 * 1. If the Ajax method is used, services will only be checked while
23 * Ajax requests are being received, which is currently only when users are
24 * logged in.
25 * 2. All services are checked and called sequentially in the order specified
26 * by the sort_order field in the background_services table. Service calls that are "slow"
27 * should be given a higher sort_order value.
28 * 3. The actual interval between two calls to a given background service may be
29 * as long as the time to complete that service plus the interval between
30 * n+1 calls to this script where n is the number of other services preceding it
31 * in the array, even if the specified minimum interval is shorter, so plan
32 * accordingly. Example: with a 5 min cron interval, the 4th service on the list
33 * may not be started again for up to 20 minutes after it has completed if
34 * services 1, 2, and 3 take more than 15, 10, and 5 minutes to complete,
35 * respectively.
37 * Copyright (C) 2013 EMR Direct <http://www.emrdirect.com/>
39 * LICENSE: This program is free software; you can redistribute it and/or
40 * modify it under the terms of the GNU General Public License
41 * as published by the Free Software Foundation; either version 2
42 * of the License, or (at your option) any later version.
43 * This program is distributed in the hope that it will be useful,
44 * but WITHOUT ANY WARRANTY; without even the implied warranty of
45 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
46 * GNU General Public License for more details.
47 * You should have received a copy of the GNU General Public License
48 * along with this program. If not, see <http://opensource.org/licenses/gpl-license.php>;.
50 * @package OpenEMR
51 * @author EMR Direct <http://www.emrdirect.com/>
52 * @link http://www.open-emr.org
55 //SANITIZE ALL ESCAPES
56 $sanitize_all_escapes=true;
58 //STOP FAKE REGISTER GLOBALS
59 $fake_register_globals=false;
61 //ajax param should be set by calling ajax scripts
62 $isAjaxCall = isset($_POST['ajax']);
64 //if false, we may assume this is a cron job and set up accordingly
65 if (!$isAjaxCall) {
66 $ignoreAuth = 1;
67 //process optional arguments when called from cron
68 $_GET['site'] = (isset($argv[1])) ? $argv[1] : 'default';
69 if (isset($argv[2]) && $argv[2]!='all') $_GET['background_service'] = $argv[2];
70 if (isset($argv[3]) && $argv[3]=='1') $_GET['background_force'] = 1;
73 //an additional require file can be specified for each service in the background_services table
74 require_once(dirname(__FILE__) . "/../../interface/globals.php");
75 require_once(dirname(__FILE__) . "/../sql.inc");
77 //Remove time limit so script doesn't time out
78 set_time_limit(0);
80 //Release session lock to prevent freezing of other scripts
81 session_write_close();
83 //Safety in case one of the background functions tries to output data
84 ignore_user_abort(1);
86 /**
87 * Execute background services
88 * This function reads a list of available services from the background_services table
89 * For each service that is not already running and is due for execution, the associated
90 * background function is run.
92 * Note: Each service must do its own logging, as appropriate, and should disable itself
93 * to prevent continued service calls if an error condition occurs which requires
94 * administrator intervention. Any service function return values and output are ignored.
97 function execute_background_service_calls() {
98 /**
99 * Note: The global $service_name below is set to the name of the service currently being
100 * processed before the actual service function call, and is unset after normal
101 * completion of the loop. If the script exits abnormally, the shutdown_function
102 * uses the value of $service_name to do any required clean up.
104 global $service_name;
106 $single_service = isset($_REQUEST['background_service']) ? $_REQUEST['background_service'] : '';
107 $force = (isset($_REQUEST['background_force']) && $_REQUEST['background_force']);
109 $sql = 'SELECT * FROM background_services WHERE ' . ($force ? '1' : 'execute_interval > 0');
110 if ($single_service!="")
111 $services = sqlStatementNoLog($sql.' AND name=?',array($single_service));
112 else
113 $services = sqlStatementNoLog($sql.' ORDER BY sort_order');
115 while($service = sqlFetchArray($services)){
116 $service_name = $service['name'];
117 if(!$service['active'] || $service['running'] == 1) continue;
118 $interval=(int)$service['execute_interval'];
120 //leverage locking built-in to UPDATE to prevent race conditions
121 //will need to assess performance in high concurrency setting at some point
122 $sql='UPDATE background_services SET running = 1, next_run = NOW()+ INTERVAL ?'
123 . ' MINUTE WHERE running < 1 ' . ($force ? '' : 'AND NOW() > next_run ') . 'AND name = ?';
124 if(sqlStatementNoLog($sql,array($interval,$service_name))===FALSE) continue;
125 $acquiredLock = mysql_affected_rows($GLOBALS['dbh']);
126 if($acquiredLock<1) continue; //service is already running or not due yet
128 if ($service['require_once'])
129 require_once($GLOBALS['fileroot'] . $service['require_once']);
130 if (!function_exists($service['function'])) continue;
132 //use try/catch in case service functions throw an unexpected Exception
133 try {
134 $service['function']();
135 } catch (Exception $e) {
136 //do nothing
139 $sql = 'UPDATE background_services SET running = 0 WHERE name = ?';
140 $res = sqlStatementNoLog($sql, array($service_name));
146 * Catch unexpected failures.
148 * if the global $service_name is still set, then a die() or exit() occurred during the execution
149 * of that service's function call, and we did not complete the foreach loop properly,
150 * so we need to reset the is_running flag for that service before quitting
153 function background_shutdown() {
154 global $service_name;
155 if (isset($service_name)) {
157 $sql = 'UPDATE background_services SET running = 0 WHERE name = ?';
158 $res = sqlStatementNoLog($sql, array($service_name));
163 register_shutdown_function('background_shutdown');
164 execute_background_service_calls();
165 unset($service_name);