4 use Symfony\Component\Process\PhpExecutableFinder
;
5 use Symfony\Component\Process\Process
;
11 * Perform a series of external, asynchronous, concurrent API call.
15 * The time to wait when polling for process status (microseconds).
17 const POLL_INTERVAL
= 10000;
21 * Array(int $idx => array $apiCall).
25 protected $defaultParams;
29 protected $settingsPath;
31 protected $env = array();
35 * Array(int $idx => Process $process).
41 * Array(int $idx => array $apiResult).
43 protected $apiResults;
46 * @param array $defaultParams
47 * Default values to merge into any API calls.
49 public function __construct($defaultParams = array()) {
51 $this->root
= $civicrm_root;
52 $this->settingsPath
= defined('CIVICRM_SETTINGS_PATH') ? CIVICRM_SETTINGS_PATH
: NULL;
53 $this->defaultParams
= $defaultParams;
57 * @param string $entity
58 * @param string $action
59 * @param array $params
62 public function addCall($entity, $action, $params = array()) {
63 $params = array_merge($this->defaultParams
, $params);
65 $this->apiCalls
[] = array(
75 * List of environment variables to add.
78 public function addEnv($env) {
79 $this->env
= array_merge($this->env
, $env);
84 * Run all the API calls concurrently.
87 * @throws \CRM_Core_Exception
89 public function start() {
90 foreach ($this->apiCalls
as $idx => $apiCall) {
91 $process = $this->createProcess($apiCall);
93 $this->processes
[$idx] = $process;
100 * The number of running processes.
102 public function getRunningCount() {
104 foreach ($this->processes
as $process) {
105 if ($process->isRunning()) {
112 public function wait() {
113 while (!empty($this->processes
)) {
114 usleep(self
::POLL_INTERVAL
);
115 foreach (array_keys($this->processes
) as $idx) {
116 /** @var Process $process */
117 $process = $this->processes
[$idx];
118 if (!$process->isRunning()) {
119 $parsed = json_decode($process->getOutput(), TRUE);
120 if ($process->getExitCode() ||
$parsed === NULL) {
121 $this->apiResults
[] = array(
123 'error_message' => 'External API returned malformed response.',
125 'code' => $process->getExitCode(),
126 'stdout' => $process->getOutput(),
127 'stderr' => $process->getErrorOutput(),
132 $this->apiResults
[] = $parsed;
134 unset($this->processes
[$idx]);
144 public function getResults() {
145 return $this->apiResults
;
152 public function getResult($idx = 0) {
153 return $this->apiResults
[$idx];
157 * Determine if the local environment supports running API calls
162 public function isSupported() {
163 // If you try in Windows, feel free to change this...
164 if (strtoupper(substr(PHP_OS
, 0, 3)) === 'WIN' ||
!function_exists('proc_open')) {
167 if (!file_exists($this->root
. '/bin/cli.php') ||
!file_exists($this->settingsPath
)) {
174 * @param array $apiCall
175 * Array with keys: entity, action, params.
177 * @throws \CRM_Core_Exception
179 public function createProcess($apiCall) {
182 $executableFinder = new PhpExecutableFinder();
183 $php = $executableFinder->find();
185 throw new \
CRM_Core_Exception("Failed to locate PHP interpreter.");
189 $parts[] = escapeshellarg($this->root
. '/bin/cli.php');
190 $parts[] = escapeshellarg("-e=" . $apiCall['entity']);
191 $parts[] = escapeshellarg("-a=" . $apiCall['action']);
193 $parts[] = escapeshellarg("-u=dummyuser");
194 foreach ($apiCall['params'] as $key => $value) {
195 $parts[] = escapeshellarg("--$key=$value");
197 $command = implode(" ", $parts);
199 $env = array_merge($this->env
, array(
200 'CIVICRM_SETTINGS' => $this->settingsPath
,
202 return new Process($command, $this->root
, $env);
208 public function getRoot() {
213 * @param string $root
215 public function setRoot($root) {
222 public function getSettingsPath() {
223 return $this->settingsPath
;
227 * @param string $settingsPath
229 public function setSettingsPath($settingsPath) {
230 $this->settingsPath
= $settingsPath;