3v4l.org

run code in 150+ php & hhvm versions
Bugs & Features
<?php function extract_docblocks($data) { $inBlock = false; $forceNewItem = true; $currentBlock = array(); $blocks = array(); foreach (preg_split('/[\r\n]+/', $data) as $line) { $line = trim($line); if (!$inBlock) { if ($line === '/**') { $inBlock = true; } continue; } if ($line[0] !== '*') { $inBlock = false; $forceNewItem = true; $currentBlock = array(); continue; } if ($line === '*/') { $inBlock = false; $forceNewItem = true; $blocks[] = $currentBlock; $currentBlock = array(); continue; } $line = trim(substr($line, 1)); if ($line === '') { $forceNewItem = true; } else if ($line[0] === '@' || $forceNewItem) { $forceNewItem = false; $currentBlock[] = $line; } else { $currentBlock[count($currentBlock) - 1] .= "\n" . $line; } } return $blocks; } $str = <<<'CLASS' <?php /** * Class EventLoop * * This is not the fastest event loop implementation in the world, at least * partially because it's been stupidified for PHP4. It's not really for high * performance asyncSaucezOMG though, so it shouldn't matter. */ class EventLoop { /** * @var EventLoopConfig */ var $config; /** * @var EventLoopLogger * @access private */ var $_logger; /** * @var WorkerPoolFactory */ var $_workerPoolFactory; /** * @var array * @access private */ var $_streamWatchers = array( 'read' => array( 'streams' => array(), 'callbacks' => array(), ), 'write' => array( 'streams' => array(), 'callbacks' => array(), ), 'except' => array( 'streams' => array(), 'callbacks' => array(), ), ); /** * @var int * @access private */ var $_streamWatcherCount = 0; /** * @var Scheduler * @access private */ var $_scheduler; /** * @var WorkerPool * @access private */ var $_dynamicWorkerPool; /** * @var WorkerPool * @access private */ var $_dnsWorkerPool; /** * @var StreamHandler[] * @access private */ var $_streamHandlers = array(); /** * @var bool * @access private */ var $_running = false; /** * Constructor * * @param WorkerPoolFactory $workerPoolFactory * @param EventLoopConfig $config * @param EventLoopLogger $logger * @param SchedulerFactory $schedulerFactory * @param StreamHandler $socketStreamHandler */ function EventLoop(&$workerPoolFactory, &$config, &$logger, &$schedulerFactory, &$socketStreamHandler) { $this->config = &$config; $this->_logger = &$logger; $this->_workerPoolFactory = &$workerPoolFactory; $this->_scheduler = &$schedulerFactory->create($this->_logger); $this->registerStreamHandler($socketStreamHandler, array('tcp', 'udp', 'unix')); $this->_dynamicWorkerPool = &$workerPoolFactory->create($this, $this->config->workers, $this->_logger, 'Dynamic'); $config->dns->normalizeInheritableProperties($this->config->workers); $this->_dnsWorkerPool = &$workerPoolFactory->create($this, $this->config->dns, $this->_logger, 'DNS'); } /** * Get the current time as a float * * @return float * @access private */ function _now() { list($usec, $sec) = explode(' ', microtime()); return ((float)$usec + (float)$sec); } /** * Check whether at least one client socket or scheduled item is active * * @return bool * @access private */ function _isActive() { return $this->_streamWatcherCount || $this->_scheduler->isActive(); } /** * Get the time until there is something to do * * @return float|null * @access private */ function _getNextActivityTime() { $candidates = array(); if (null !== $time = $this->_scheduler->getNextActivityTime()) { $candidates[] = $time; } if (null !== $time = $this->_dynamicWorkerPool->getNextActivityTime()) { $candidates[] = $time; } if (null !== $time = $this->_dnsWorkerPool->getNextActivityTime()) { $candidates[] = $time; } return $candidates ? max(min($candidates), 0) : null; } /** * Sleep until the next scheduled item is due to be executed * * @access private */ function _awaitNextActivity($timeout) { if ($timeout) { $this->_logger->debug(null, 'Waiting until next scheduled activity, timeout: %f', $timeout); usleep(floor($timeout * 1000000)); } } /** * Process I/O on all active streams * * @access private */ function _processStreams($timeout) { $streams = array( 'read' => $this->_streamWatchers['read']['streams'], 'write' => $this->_streamWatchers['write']['streams'], 'except' => $this->_streamWatchers['except']['streams'], ); if ($timeout !== null) { $secs = floor($timeout); $usecs = floor(($timeout - $secs) * 1000000); } else { $secs = $usecs = null; } $this->_logger->debug( null, 'select() watching %d streams for activity (read: %d, write: %d, except: %d), timeout: %s', count($streams['read']) + count($streams['write']) + count($streams['except']), count($streams['read']), count($streams['write']), count($streams['except']), $secs === null ? 'NULL' : "{$secs}s {$usecs}u" ); $count = stream_select($streams['read'], $streams['write'], $streams['except'], $secs, $usecs); if ($count === false) { $this->_logger->error(null, 'select() operation failed!'); exit(1); } else if ($count === 0) { $this->_logger->debug(null, 'select() returned 0 streams with activity'); return; } $this->_logger->debug( null, 'select() returned %d streams with activity (read: %d, write: %d, except: %d)', $count, count($streams['read']), count($streams['write']), count($streams['except']) ); foreach (array('read', 'write', 'except') as $op) { foreach ($streams[$op] as $stream) { $id = (int) $stream; if (isset($this->_streamWatchers[$op]['callbacks'][$id])) { call_user_func($this->_streamWatchers[$op]['callbacks'][$id], $stream); } } } } /** * Add a watcher for a stream * * @param string $op * @param resource $stream * @param callable $callback * @return bool * @access module */ function _addStreamWatcher($op, $stream, $callback) { $id = (int) $stream; $op = strtolower($op); if (!isset($this->_streamWatchers[$op])) { $this->_logger->debug(null, 'Failed to add %s watcher on stream #%d: unknown op', $op, $id); return false; } else if (!is_resource($stream)) { $this->_logger->debug(null, 'Failed to add %s watcher on stream #%d: not a valid stream resource', $op, $id); return false; } else if (!is_callable($callback)) { $this->_logger->debug(null, 'Failed to add %s watcher on stream #%d: invalid callback', $op, $id); return false; } if (!isset($this->_streamWatchers[$op]['streams'][$id])) { $this->_streamWatchers[$op]['streams'][$id] = $stream; $this->_streamWatcherCount++; } else { $this->_logger->debug(null, 'Overwrote %s watcher on stream #%d', $op, $id); } $this->_streamWatchers[$op]['callbacks'][$id] = $callback; $this->_logger->debug(null, 'Added %s watcher on stream #%d, watchers: %d', $op, $id, $this->_streamWatcherCount); return true; } /** * Remove a watcher for a stream or stream ID * * @param string $op * @param resource|int $stream * @access module */ function _removeStreamWatcher($op, $stream) { $id = (int) $stream; $op = strtolower($op); if (isset($this->_streamWatchers[$op]['streams'][$id])) { unset($this->_streamWatchers[$op]['streams'][$id], $this->_streamWatchers[$op]['callbacks'][$id]); $this->_streamWatcherCount--; $this->_logger->debug(null, 'Removed %s watcher on stream #%d, watchers: %d', $op, $id, $this->_streamWatcherCount); } else { $this->_logger->debug(null, 'Could not remove %s watcher on stream #%d: not registered', $op, $id); } } /** * Clean up and shut down the event loop */ function _shutdown() { $this->_logger->info(null, 'Shutting down event loop'); // Remove all stream watchers apart from workers foreach (array('read', 'write', 'except') as $op) { foreach ($this->_streamWatchers[$op]['callbacks'] as $id => $callback) { if (!is_array($callback) || !is_a($callback[0], 'WorkerParentEndpoint')) { $this->_removeStreamWatcher($op, $id); } } } // Empty the schedule $this->_scheduler->shutdown(); // Send all workers a terminate signal $this->_dynamicWorkerPool->shutdown(); $this->_dnsWorkerPool->shutdown(); // Let workers terminate gracefully while ($this->_streamWatcherCount > 0) { $this->_processStreams($this->_getNextActivityTime()); $this->_scheduler->processActivity(); } } /** * Register a handler for a stream wrapper or set of wrappers * * @param StreamHandler $handler * @param array $schemes * @return bool */ function registerStreamHandler(&$handler, $schemes) { if (!is_a($handler, 'StreamHandler')) { return false; } foreach ($schemes as $scheme) { $this->_streamHandlers[strtolower($scheme)] = &$handler; } return true; } /** * Create a new stream * * @param string $address * @return SocketStream */ function &openStream($address) { if (!$url = parse_url($address)) { $this->_logger->warn(null, 'Unable to parse stream URL %s: Parse failed', $address); return null; } else if (!isset($url['scheme'])) { $this->_logger->warn(null, 'Unable to parse stream URL %s: Missing scheme', $address); return null; } $scheme = strtolower($url['scheme']); if (!isset($this->_streamHandlers[$scheme])) { $this->_logger->warn(null, 'No stream handler registered for URI scheme: %s', $scheme); return null; } $stream = &$this->_streamHandlers[$scheme]->createStream($this, $url); return $stream; } /** * Schedule a job to be run asynchronously in a worker * * @param string $method * @param mixed ...$args * @return AsyncJob */ function &async($method) { $args = func_get_args(); $job = &new AsyncJob($method, array_slice($args, 1)); $this->_dynamicWorkerPool->pushJob($job); $this->_logger->info(null, 'Queued async job, method: %s', $method); return $job; } /** * Resolve a host name to an IP address asynchronously * * @param string $host * @param callable $callback * @param bool $returnHost */ function resolveHost($host, $callback, $returnHost = false) { if ($host === '255.255.255.255' || ip2long($host) !== -1) { $this->in(0, $callback, $returnHost ? array($host, $host) : array($host)); } else { $job = &new AsyncJob('DNSWorker::resolve', array($host)); $this->_dnsWorkerPool->pushJob($job); $job->on('complete', $callback, $returnHost ? array($host) : array()); $job->on('error', $callback, $returnHost ? array($host, null) : array(null)); } } /** * Schedule a callback to be execute after a specified number of microseconds * * @param int $usecs * @param callable $callback * @param array $args * @return int */ function in($usecs, $callback, $args = array()) { return $this->_scheduler->createItem($this->_now() + ($usecs / 1000000), $callback, $args); } /** * Schedule a callback to execute at a specified timestamp * * @param float $timestamp * @param callable $callback * @param array $args * @return int */ function at($timestamp, $callback, $args = array()) { return $this->_scheduler->createItem($timestamp, $callback, $args); } /** * Schedule a recurring callback to execute after a specified number of microseconds * * @param int $usecs * @param callable $callback * @param array $args * @return int */ function every($usecs, $callback, $args = array()) { $secs = $usecs / 1000000; return $this->_scheduler->createItem($this->_now() + $secs, $callback, $args, $secs); } /** * Cancel a scheduled callback * * @param int $id * @return bool */ function cancel($id) { return $this->_scheduler->removeItem($id); } /** * Execute the event loop */ function run() { $this->_logger->info(null, 'Starting event loop'); $this->_running = true; // Main loop while ($this->_running && $this->_isActive()) { $timeout = $this->_getNextActivityTime(); if ($this->_streamWatcherCount > 0) { $this->_processStreams($timeout); } else { $this->_awaitNextActivity($timeout); } $this->_dynamicWorkerPool->processActivity(); $this->_dnsWorkerPool->processActivity(); $this->_scheduler->processActivity(); } $this->_running = false; $this->_shutdown(); $this->_logger->info(null, 'Event loop terminated'); } /** * Check whether the event loop is running * * @return bool */ function isRunning() { return $this->_running; } /** * Stop the event loop after the current iteration */ function stop() { $this->_logger->debug(null, 'Stopping event loop'); $this->_running = false; } } CLASS; print_r(extract_docblocks($str));
Output for 5.3.0 - 7.1.0
Array ( [0] => Array ( [0] => Class EventLoop [1] => This is not the fastest event loop implementation in the world, at least partially because it's been stupidified for PHP4. It's not really for high performance asyncSaucezOMG though, so it shouldn't matter. ) [1] => Array ( [0] => @var EventLoopConfig ) [2] => Array ( [0] => @var EventLoopLogger [1] => @access private ) [3] => Array ( [0] => @var WorkerPoolFactory ) [4] => Array ( [0] => @var array [1] => @access private ) [5] => Array ( [0] => @var int [1] => @access private ) [6] => Array ( [0] => @var Scheduler [1] => @access private ) [7] => Array ( [0] => @var WorkerPool [1] => @access private ) [8] => Array ( [0] => @var WorkerPool [1] => @access private ) [9] => Array ( [0] => @var StreamHandler[] [1] => @access private ) [10] => Array ( [0] => @var bool [1] => @access private ) [11] => Array ( [0] => Constructor [1] => @param WorkerPoolFactory $workerPoolFactory [2] => @param EventLoopConfig $config [3] => @param EventLoopLogger $logger [4] => @param SchedulerFactory $schedulerFactory [5] => @param StreamHandler $socketStreamHandler ) [12] => Array ( [0] => Get the current time as a float [1] => @return float [2] => @access private ) [13] => Array ( [0] => Check whether at least one client socket or scheduled item is active [1] => @return bool [2] => @access private ) [14] => Array ( [0] => Get the time until there is something to do [1] => @return float|null [2] => @access private ) [15] => Array ( [0] => Sleep until the next scheduled item is due to be executed [1] => @access private ) [16] => Array ( [0] => Process I/O on all active streams [1] => @access private ) [17] => Array ( [0] => Add a watcher for a stream [1] => @param string $op [2] => @param resource $stream [3] => @param callable $callback [4] => @return bool [5] => @access module ) [18] => Array ( [0] => Remove a watcher for a stream or stream ID [1] => @param string $op [2] => @param resource|int $stream [3] => @access module ) [19] => Array ( [0] => Clean up and shut down the event loop ) [20] => Array ( [0] => Register a handler for a stream wrapper or set of wrappers [1] => @param StreamHandler $handler [2] => @param array $schemes [3] => @return bool ) [21] => Array ( [0] => Create a new stream [1] => @param string $address [2] => @return SocketStream ) [22] => Array ( [0] => Schedule a job to be run asynchronously in a worker [1] => @param string $method [2] => @param mixed ...$args [3] => @return AsyncJob ) [23] => Array ( [0] => Resolve a host name to an IP address asynchronously [1] => @param string $host [2] => @param callable $callback [3] => @param bool $returnHost ) [24] => Array ( [0] => Schedule a callback to be execute after a specified number of microseconds [1] => @param int $usecs [2] => @param callable $callback [3] => @param array $args [4] => @return int ) [25] => Array ( [0] => Schedule a callback to execute at a specified timestamp [1] => @param float $timestamp [2] => @param callable $callback [3] => @param array $args [4] => @return int ) [26] => Array ( [0] => Schedule a recurring callback to execute after a specified number of microseconds [1] => @param int $usecs [2] => @param callable $callback [3] => @param array $args [4] => @return int ) [27] => Array ( [0] => Cancel a scheduled callback [1] => @param int $id [2] => @return bool ) [28] => Array ( [0] => Execute the event loop ) [29] => Array ( [0] => Check whether the event loop is running [1] => @return bool ) [30] => Array ( [0] => Stop the event loop after the current iteration ) )
Output for 4.4.2 - 4.4.9, 5.1.0 - 5.2.17
Parse error: syntax error, unexpected T_SL in /in/o3aZb on line 52
Process exited with code 255.
Output for 4.3.0 - 4.3.1, 4.3.5 - 4.4.1, 5.0.0 - 5.0.5
Parse error: parse error, unexpected T_SL in /in/o3aZb on line 52
Process exited with code 255.
Output for 4.3.2 - 4.3.4
Parse error: parse error in /in/o3aZb on line 52
Process exited with code 255.