array( 'pipe', 'r' ), 1 => array( 'pipe', 'w' ), 2 => array( 'pipe', 'w' ), ), $pipes, $dir, array( 'LC_ALL' => 'C', 'PATH' => '/usr/bin:/bin', 'TMPDIR' => $dir, ), array( 'bypass_shell' => true ) ); if ( ! is_resource( $proc ) ) { throw new \RuntimeException( 'ghostscript_unavailable' ); } // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- Ghostscript reads no standard input; its pipe is closed at once. fclose( $pipes[0] ); unset( $pipes[0] ); $out = ''; $err = ''; $until = microtime( true ) + $seconds; $status = null; foreach ( $pipes as $pipe ) { stream_set_blocking( $pipe, false ); } try { do { $out .= (string) stream_get_contents( $pipes[1] ); $err .= (string) stream_get_contents( $pipes[2] ); if ( strlen( $out ) + strlen( $err ) > self::MAX_LOG ) { throw new \RuntimeException( 'ghostscript_output_limit' ); } if ( '' !== $watch ) { clearstatcache( true, $watch ); if ( is_file( $watch ) && filesize( $watch ) > $limit ) { throw new \RuntimeException( 'ghostscript_output_limit' ); } } $status = proc_get_status( $proc ); if ( ! $status['running'] ) { break; } if ( microtime( true ) >= $until ) { throw new \RuntimeException( 'ghostscript_timeout' ); } usleep( 20000 ); } while ( true ); $out .= (string) stream_get_contents( $pipes[1] ); $err .= (string) stream_get_contents( $pipes[2] ); } finally { $st = proc_get_status( $proc ); if ( $st['running'] ) { proc_terminate( $proc, 9 ); } foreach ( $pipes as $pipe ) { if ( is_resource( $pipe ) ) { // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- Ghostscript's own output pipes, closed after each bounded run. fclose( $pipe ); } } proc_close( $proc ); } return array( 'exit' => (int) ( $status['exitcode'] ?? -1 ), 'out' => $out, 'err' => $err, ); } }