3v4l.org

run code in 300+ PHP versions simultaneously
<?php namespace RefactoringGuru\Command\RealWorld; /** * Интерфейс Команды объявляет основной метод выполнения, а также несколько * вспомогательных методов для получения метаданных команды. */ interface Command { public function execute(): void; public function getId(): int; public function getStatus(): int; } /** * Базовая Команда скрейпинга устанавливает базовую инфраструктуру загрузки, * общую для всех конкретных команд скрейпинга. */ abstract class WebScrapingCommand implements Command { public $id; public $status = 0; /** * @var string URL для скрейпинга. */ public $url; public function __construct(string $url) { $this->url = $url; } public function getId(): int { return $this->id; } public function getStatus(): int { return $this->status; } public function getURL(): string { return $this->url; } /** * Поскольку методы выполнения для всех команд скрейпинга очень похожи, мы * можем предоставить реализацию по умолчанию, позволив подклассам * переопределить её при необходимости. * * Шш! Наблюдательный читатель может обнаружить здесь другой поведенческий * паттерн в действии. */ public function execute(): void { $html = $this->download(); $this->parse($html); $this->complete(); } public function download(): string { $html = file_get_contents($this->getURL()); echo "WebScrapingCommand: Downloaded {$this->url}\n"; return $html; } abstract public function parse(string $html): void; public function complete(): void { $this->status = 1; Queue::get()->completeCommand($this); } } /** * Конкретная Команда для извлечения списка жанров фильма. */ class IMDBGenresScrapingCommand extends WebScrapingCommand { public function __construct() { $this->url = "https://www.imdb.com/feature/genre/"; } /** * Извлечение всех жанров и их поисковых URL со страницы: * https://www.imdb.com/feature/genre/ */ public function parse($html): void { preg_match_all("|href=\"(https://www.imdb.com/search/title\?genres=.*?)\"|", $html, $matches); echo "IMDBGenresScrapingCommand: Discovered " . count($matches[1]) . " genres.\n"; foreach ($matches[1] as $genre) { Queue::get()->add(new IMDBGenrePageScrapingCommand($genre)); } } } /** * Конкретная Команда для извлечения списка фильмов определённого жанра. */ class IMDBGenrePageScrapingCommand extends WebScrapingCommand { private $page; public function __construct(string $url, int $page = 1) { parent::__construct($url); $this->page = $page; } public function getURL(): string { return $this->url . '?page=' . $this->page; } /** * Извлечение всех фильмов со страницы вроде этой: * https://www.imdb.com/search/title?genres=sci-fi&explore=title_type,genres */ public function parse(string $html): void { preg_match_all("|href=\"(/title/.*?/)\?ref_=adv_li_tt\"|", $html, $matches); echo "IMDBGenrePageScrapingCommand: Discovered " . count($matches[1]) . " movies.\n"; foreach ($matches[1] as $moviePath) { $url = "https://www.imdb.com" . $moviePath; Queue::get()->add(new IMDBMovieScrapingCommand($url)); } // Извлечение URL следующей страницы. if (preg_match("|Next &#187;</a>|", $html)) { Queue::get()->add(new IMDBGenrePageScrapingCommand($this->url, $this->page + 1)); } } } /** * Конкретная Команда для извлечения подробных сведений о фильме. */ class IMDBMovieScrapingCommand extends WebScrapingCommand { /** * Получить информацию о фильме с подобной страницы: * https://www.imdb.com/title/tt4154756/ */ public function parse(string $html): void { if (preg_match("|<h1 itemprop=\"name\" class=\"\">(.*?)</h1>|", $html, $matches)) { $title = $matches[1]; } echo "IMDBMovieScrapingCommand: Parsed movie $title.\n"; } } /** * Класс Очередь действует как Отправитель. Он складывает объекты команд в стек * и выполняет их поочерёдно. Если выполнение скрипта внезапно завершится, * очередь и все её команды можно будет легко восстановить, и вам не придётся * повторять все выполненные команды. * * Обратите внимание, что это очень примитивная реализация очереди команд, * которая хранит команды в локальной базе данных SQLite. Существуют десятки * надёжных реализаций очереди, доступных для использования в реальных * приложениях. */ class Queue { private $db; public function __construct() { $this->db = new \SQLite3(__DIR__ . '/commands.sqlite', SQLITE3_OPEN_CREATE | SQLITE3_OPEN_READWRITE); $this->db->query('CREATE TABLE IF NOT EXISTS "commands" ( "id" INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL, "command" TEXT, "status" INTEGER )'); } public function isEmpty(): bool { $query = 'SELECT COUNT("id") FROM "commands" WHERE status = 0'; return $this->db->querySingle($query) === 0; } public function add(Command $command): void { $query = 'INSERT INTO commands (command, status) VALUES (:command, :status)'; $statement = $this->db->prepare($query); $statement->bindValue(':command', base64_encode(serialize($command))); $statement->bindValue(':status', $command->getStatus()); $statement->execute(); } public function getCommand(): Command { $query = 'SELECT * FROM "commands" WHERE "status" = 0 LIMIT 1'; $record = $this->db->querySingle($query, true); $command = unserialize(base64_decode($record["command"])); $command->id = $record['id']; return $command; } public function completeCommand(Command $command): void { $query = 'UPDATE commands SET status = :status WHERE id = :id'; $statement = $this->db->prepare($query); $statement->bindValue(':status', $command->getStatus()); $statement->bindValue(':id', $command->getId()); $statement->execute(); } public function work(): void { while (!$this->isEmpty()) { $command = $this->getCommand(); $command->execute(); } } /** * Для удобства объект Очереди является Одиночкой. */ public static function get(): Queue { static $instance; if (!$instance) { $instance = new Queue(); } return $instance; } } /** * Клиентский код. */ $queue = Queue::get(); if ($queue->isEmpty()) { $queue->add(new IMDBGenresScrapingCommand()); } $queue->work();

Here you find the average performance (time & memory) of each version. A grayed out version indicates it didn't complete successfully (based on exit-code).

VersionSystem time (s)User time (s)Memory (MiB)
8.5.10.0130.00717.29
8.5.00.0160.00920.47
8.4.150.0020.00014.05
8.4.140.0140.00718.00
8.4.130.0090.01318.34
8.4.120.0050.00720.72
8.4.110.0120.01023.02
8.4.100.0050.00519.14
8.4.90.0130.01019.46
8.4.80.0090.00621.08
8.4.70.0150.00718.78
8.4.60.0100.00919.24
8.4.50.0100.00519.30
8.4.40.0180.00020.23
8.4.30.0030.00520.84
8.4.20.0060.00321.36
8.4.10.0000.01020.00
8.3.280.0120.00918.95
8.3.270.0120.00917.21
8.3.260.0110.01017.26
8.3.250.0120.00819.48
8.3.240.0150.00417.68
8.3.230.0120.00917.18
8.3.220.0040.00419.66
8.3.210.0100.01017.23
8.3.200.0040.00417.24
8.3.190.0130.00617.69
8.3.180.0120.00818.79
8.3.170.0140.00421.27
8.3.160.0100.01017.66
8.3.150.0190.00017.71
8.3.140.0110.00717.12
8.3.130.0060.00317.30
8.3.120.0040.00419.59
8.3.110.0060.00320.94
8.3.100.0060.00324.06
8.3.90.0080.00026.77
8.3.80.0050.00517.38
8.3.70.0080.00817.25
8.3.60.0070.01417.13
8.3.50.0140.01018.62
8.3.40.0060.00919.52
8.3.30.0040.01119.21
8.3.20.0040.00424.18
8.3.10.0040.00424.66
8.3.00.0000.00826.16
8.2.290.0060.00220.99
8.2.280.0100.01019.09
8.2.270.0000.00817.71
8.2.260.0040.01517.21
8.2.250.0030.00719.07
8.2.240.0060.00318.82
8.2.230.0090.00022.58
8.2.220.0120.00037.54
8.2.210.0080.00026.77
8.2.200.0030.00617.25
8.2.190.0190.00017.38
8.2.180.0130.00325.92
8.2.170.0120.00619.64
8.2.160.0110.01122.96
8.2.150.0040.00425.66
8.2.140.0060.00324.66
8.2.130.0050.00326.16
8.2.120.0040.00426.16
8.2.110.0070.00421.16
8.2.100.0070.00421.25
8.1.330.0120.01022.45
8.1.320.0030.00416.67
8.1.310.0040.01517.11
8.1.300.0080.00418.57
8.1.290.0040.00818.88
8.1.280.0120.00625.92
8.1.270.0040.00423.99
8.1.260.0080.00026.35
8.1.250.0040.00428.09
8.1.240.0070.00420.60
8.1.230.0040.00422.81
8.0.90.0030.00517.44

preferences:
41.49 ms | 403 KiB | 5 Q