ServerClientTestCase.inc 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. <?php
  2. const WORKER_ARGV_VALUE = 'RUN_WORKER';
  3. const WORKER_DEFAULT_NAME = 'server';
  4. function phpt_notify($worker = WORKER_DEFAULT_NAME)
  5. {
  6. ServerClientTestCase::getInstance()->notify($worker);
  7. }
  8. function phpt_wait($worker = WORKER_DEFAULT_NAME, $timeout = null)
  9. {
  10. ServerClientTestCase::getInstance()->wait($worker, $timeout);
  11. }
  12. function phpt_has_sslv3() {
  13. static $result = null;
  14. if (!is_null($result)) {
  15. return $result;
  16. }
  17. $server = @stream_socket_server('sslv3://127.0.0.1:10013');
  18. if ($result = !!$server) {
  19. fclose($server);
  20. }
  21. return $result;
  22. }
  23. /**
  24. * This is a singleton to let the wait/notify functions work
  25. * I know it's horrible, but it's a means to an end
  26. */
  27. class ServerClientTestCase
  28. {
  29. private $isWorker = false;
  30. private $workerHandle = [];
  31. private $workerStdIn = [];
  32. private $workerStdOut = [];
  33. private static $instance;
  34. public static function getInstance($isWorker = false)
  35. {
  36. if (!isset(self::$instance)) {
  37. self::$instance = new self($isWorker);
  38. }
  39. return self::$instance;
  40. }
  41. public function __construct($isWorker = false)
  42. {
  43. if (!isset(self::$instance)) {
  44. self::$instance = $this;
  45. }
  46. $this->isWorker = $isWorker;
  47. }
  48. private function spawnWorkerProcess($worker, $code)
  49. {
  50. if (defined("PHP_WINDOWS_VERSION_MAJOR")) {
  51. $ini = php_ini_loaded_file();
  52. $cmd = sprintf(
  53. '%s %s "%s" %s',
  54. PHP_BINARY, $ini ? "-n -c $ini" : "",
  55. __FILE__,
  56. WORKER_ARGV_VALUE
  57. );
  58. } else {
  59. $cmd = sprintf(
  60. '%s "%s" %s %s',
  61. PHP_BINARY,
  62. __FILE__,
  63. WORKER_ARGV_VALUE,
  64. $worker
  65. );
  66. }
  67. $this->workerHandle[$worker] = proc_open(
  68. $cmd,
  69. [['pipe', 'r'], ['pipe', 'w'], STDERR],
  70. $pipes
  71. );
  72. $this->workerStdIn[$worker] = $pipes[0];
  73. $this->workerStdOut[$worker] = $pipes[1];
  74. fwrite($this->workerStdIn[$worker], $code . "\n---\n");
  75. }
  76. private function cleanupWorkerProcess($worker)
  77. {
  78. fclose($this->workerStdIn[$worker]);
  79. fclose($this->workerStdOut[$worker]);
  80. proc_close($this->workerHandle[$worker]);
  81. }
  82. private function stripPhpTagsFromCode($code)
  83. {
  84. return preg_replace('/^\s*<\?(?:php)?|\?>\s*$/i', '', $code);
  85. }
  86. public function runWorker()
  87. {
  88. $code = '';
  89. while (1) {
  90. $line = fgets(STDIN);
  91. if (trim($line) === "---") {
  92. break;
  93. }
  94. $code .= $line;
  95. }
  96. eval($code);
  97. }
  98. public function run($masterCode, $workerCode)
  99. {
  100. if (!is_array($workerCode)) {
  101. $workerCode = [WORKER_DEFAULT_NAME => $workerCode];
  102. }
  103. foreach ($workerCode as $worker => $code) {
  104. $this->spawnWorkerProcess($worker, $this->stripPhpTagsFromCode($code));
  105. }
  106. eval($this->stripPhpTagsFromCode($masterCode));
  107. foreach ($workerCode as $worker => $code) {
  108. $this->cleanupWorkerProcess($worker);
  109. }
  110. }
  111. public function wait($worker, $timeout = null)
  112. {
  113. $handle = $this->isWorker ? STDIN : $this->workerStdOut[$worker];
  114. if ($timeout === null) {
  115. fgets($handle);
  116. return true;
  117. }
  118. stream_set_blocking($handle, false);
  119. $read = [$handle];
  120. $result = stream_select($read, $write, $except, $timeout);
  121. if (!$result) {
  122. return false;
  123. }
  124. fgets($handle);
  125. stream_set_blocking($handle, true);
  126. return true;
  127. }
  128. public function notify($worker)
  129. {
  130. fwrite($this->isWorker ? STDOUT : $this->workerStdIn[$worker], "\n");
  131. }
  132. }
  133. if (isset($argv[1]) && $argv[1] === WORKER_ARGV_VALUE) {
  134. ServerClientTestCase::getInstance(true)->runWorker();
  135. }