array $apiCall). */ protected $apiCalls; protected $defaultParams; protected $root; protected $settingsPath; protected $env = array(); /** * @var array * Array(int $idx => Process $process). */ protected $processes; /** * @var array * Array(int $idx => array $apiResult). */ protected $apiResults; /** * @param array $defaultParams * Default values to merge into any API calls. */ public function __construct($defaultParams = array()) { global $civicrm_root; $this->root = $civicrm_root; $this->settingsPath = defined('CIVICRM_SETTINGS_PATH') ? CIVICRM_SETTINGS_PATH : NULL; $this->defaultParams = $defaultParams; } /** * @param string $entity * @param string $action * @param array $params * @return $this */ public function addCall($entity, $action, $params = array()) { $params = array_merge($this->defaultParams, $params); $this->apiCalls[] = array( 'entity' => $entity, 'action' => $action, 'params' => $params, ); return $this; } /** * @param array $env * List of environment variables to add. * @return static */ public function addEnv($env) { $this->env = array_merge($this->env, $env); return $this; } /** * Run all the API calls concurrently. * * @return static * @throws \CRM_Core_Exception */ public function start() { foreach ($this->apiCalls as $idx => $apiCall) { $process = $this->createProcess($apiCall); $process->start(); $this->processes[$idx] = $process; } return $this; } /** * @return int * The number of running processes. */ public function getRunningCount() { $count = 0; foreach ($this->processes as $process) { if ($process->isRunning()) { $count++; } } return $count; } public function wait() { while (!empty($this->processes)) { usleep(self::POLL_INTERVAL); foreach (array_keys($this->processes) as $idx) { /** @var Process $process */ $process = $this->processes[$idx]; if (!$process->isRunning()) { $parsed = json_decode($process->getOutput(), TRUE); if ($process->getExitCode() || $parsed === NULL) { $this->apiResults[] = array( 'is_error' => 1, 'error_message' => 'External API returned malformed response.', 'trace' => array( 'code' => $process->getExitCode(), 'stdout' => $process->getOutput(), 'stderr' => $process->getErrorOutput(), ), ); } else { $this->apiResults[] = $parsed; } unset($this->processes[$idx]); } } } return $this; } /** * @return array */ public function getResults() { return $this->apiResults; } /** * @param int $idx * @return array */ public function getResult($idx = 0) { return $this->apiResults[$idx]; } /** * Determine if the local environment supports running API calls * externally. * * @return bool */ public function isSupported() { // If you try in Windows, feel free to change this... if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' || !function_exists('proc_open')) { return FALSE; } if (!file_exists($this->root . '/bin/cli.php') || !file_exists($this->settingsPath)) { return FALSE; } return TRUE; } /** * @param array $apiCall * Array with keys: entity, action, params. * @return Process * @throws \CRM_Core_Exception */ public function createProcess($apiCall) { $parts = array(); $executableFinder = new PhpExecutableFinder(); $php = $executableFinder->find(); if (!$php) { throw new \CRM_Core_Exception("Failed to locate PHP interpreter."); } $parts[] = $php; $parts[] = escapeshellarg($this->root . '/bin/cli.php'); $parts[] = escapeshellarg("-e=" . $apiCall['entity']); $parts[] = escapeshellarg("-a=" . $apiCall['action']); $parts[] = "--json"; $parts[] = escapeshellarg("-u=dummyuser"); foreach ($apiCall['params'] as $key => $value) { $parts[] = escapeshellarg("--$key=$value"); } $command = implode(" ", $parts); $env = array_merge($this->env, array( 'CIVICRM_SETTINGS' => $this->settingsPath, )); return new Process($command, $this->root, $env); } /** * @return string */ public function getRoot() { return $this->root; } /** * @param string $root */ public function setRoot($root) { $this->root = $root; } /** * @return string */ public function getSettingsPath() { return $this->settingsPath; } /** * @param string $settingsPath */ public function setSettingsPath($settingsPath) { $this->settingsPath = $settingsPath; } }