/** * Get phpRack_Adapters_ConnectionMonitor instance * * @return phpRack_Adapters_ConnectionMonitor */ public static function getInstance() { if (self::$_instance === null) { self::$_instance = new self(); } return self::$_instance; }
public function testConnectionMonitorAndTestStop() { /** * @see phpRack_Adapters_ConnectionManager */ require_once PHPRACK_PATH . '/Adapters/ConnectionMonitor.php'; for ($i = 0; $i < 20; $i++) { sleep(1); phpRack_Adapters_ConnectionMonitor::getInstance()->ping(); } $this->assert->isTrue(true)->onSuccess('Long test: always true'); }
/** * Execute shell command. Use asynchronous pipes to communicate * with child process * * We must pass custom env from _getEnv() method to to * proc_open() call, because on phprack.com server. * When we execute external php file from other process, script * make some strange forks and execute itself many times. This * behavior results server internal error. * * shell_exec() is also affected by this issue, we have earlier * this problem, and was solved by adding "env -i" before shell * command. Recently we have added PEAR support, which internally * execute PHP script, so problem returned. * * Direct reason of this error is $_ENV['PATH'] * variable. When we unset it and pass modified env to * proc_open(). It works on Windows XP, Ubuntu Linux and solve * problem on phprack.com server. * But from some reason on MacOS there is some problem with * this env value reseting, and we lose some privilege? Due to * this fact, our unit tests fail, because PEAR test produce * error: * * <code> * touch(): Unable to create file /opt/local/lib/php/.lock * because Permission denied in /usr/local/PEAR/PEAR/Registry.php * on line 835 * </code> * * If we pass null to proc_open() env param we have no problems * on MacOS, but problem on phprack.com still exists. * * That is reason why we must pass to this function custom env * param. * * @return string Command execution output * @throws Exception if from some reason command can't be executed * @throws Exception if command process was terminated * @see phpRack_Package_Php::lint() */ public function run() { $descriptors = array(self::PIPE_STD_IN => array('pipe', 'r'), self::PIPE_STD_OUT => array('pipe', 'w')); $pipes = array(); // execute command and get its proccess resource $this->_process = proc_open($this->_command, $descriptors, $pipes, getcwd(), $this->_getEnv()); // if there was some problems with command execution if (!is_resource($this->_process)) { throw new Exception("Can't execute shell command '{$this->_command}'"); } $output = ''; // close stdin to avoid child process waiting for data fclose($pipes[self::PIPE_STD_IN]); unset($pipes[self::PIPE_STD_IN]); // set non blockiing mode for pipes foreach ($pipes as $pipe) { stream_set_blocking($pipe, false); } while (true) { $readPipes = array(); // check that we readed everything from pipes foreach ($pipes as $pipe) { if (!feof($pipe)) { $readPipes[] = $pipe; } } // we have nothing more to read if (empty($readPipes)) { break; } // set to null because we only use pipes to read $write = null; $except = null; /** * Maximum time for which stream_select() function will lock script * execution. * * This short timeout allow as to return to main script and check * connection status */ $waitTimeout = 1; // 1 second /** * Check that we have some data to read from child process pipes * * If there are some new data to read, this function will return * imediatelly. * * If there are NO new data to read, after 1 second it return with * $changedStreamsCount = 0 * * $readPipes is passed, by reference and after call will contain * ONLY pipes with new data to read */ $changedStreamsCount = stream_select($readPipes, $write, $except, $waitTimeout); // if child process was terminated if ($changedStreamsCount === false) { throw new Exception('Proccess was terminated'); } // check client connection is still opened phpRack_Adapters_ConnectionMonitor::getInstance()->ping(); // read data from pipes and attach them to output foreach ($readPipes as $pipe) { $output .= fread($pipe, 1024); } } // close pipes to avoid deadlock during proc_close() fclose($pipes[self::PIPE_STD_OUT]); // close proccess and cleanup proc_close($this->_process); $this->_process = null; return $output; }