diff --git a/composer.json b/composer.json index 361dc37e2..39a2415b1 100644 --- a/composer.json +++ b/composer.json @@ -37,6 +37,7 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.3", "maglnet/composer-require-checker": "^4.7.1", + "phpbench/phpbench": "^1.4.3", "phpunit/phpunit": "^10.5.48", "rector/rector": "^2.1.2", "spatie/phpunit-watcher": "^1.24", @@ -82,6 +83,7 @@ }, "scripts": { "test": "phpunit --testdox --no-interaction", - "test-watch": "phpunit-watcher watch" + "test-watch": "phpunit-watcher watch", + "bench": "phpbench run" } } diff --git a/docs/internals.md b/docs/internals.md index 087a514ac..4e7928cdd 100644 --- a/docs/internals.md +++ b/docs/internals.md @@ -42,3 +42,21 @@ all dependencies are correctly defined in `composer.json`. To run the checker, e ```shell ./vendor/bin/composer-require-checker ``` + +## Benchmarks + +The package provides performance benchmarks for the main use-cases, implemented with [phpbench](https://phpbench.readthedocs.io/). + +Benchmarks are located in `tests/Benchmark` and configured via `phpbench.json`. + +To run the benchmarks: + +```shell +./vendor/bin/phpbench run +``` + +Or via Composer script: + +```shell +composer bench +``` diff --git a/phpbench.json b/phpbench.json new file mode 100644 index 000000000..447d32025 --- /dev/null +++ b/phpbench.json @@ -0,0 +1,7 @@ +{ + "runner.bootstrap": "vendor/autoload.php", + "runner.path": "tests/Benchmark", + "runner.revs": 1000, + "runner.iterations": 10, + "runner.retry_threshold": 2 +} diff --git a/tests/Benchmark/BlocksBench.php b/tests/Benchmark/BlocksBench.php new file mode 100644 index 000000000..e82a80d66 --- /dev/null +++ b/tests/Benchmark/BlocksBench.php @@ -0,0 +1,74 @@ +setBlock('block-id-1', '...content of block1...'); +$this->setBlock('block-id-2', '...content of block2...'); +PHP + ); + } + + $layoutView = $basePath . '/layout.php'; + if (!is_file($layoutView)) { + file_put_contents( + $layoutView, + <<<'PHP' + +hasBlock('block-id-1')): ?> + getBlock('block-id-1') ?> + + ... default content for block1 ... + + +hasBlock('block-id-2')): ?> + getBlock('block-id-2') ?> + + ... default content for block2 ... + +PHP + ); + } + + $this->view = new View($basePath, new SimpleEventDispatcher()); + } + + public function benchRenderBlocksWithTemplates(): void + { + // Render content view to define blocks. + $this->view->render('content'); + + // Render layout view which reads the defined blocks. + $this->view->render('layout'); + } +} diff --git a/tests/Benchmark/MultiRendererBench.php b/tests/Benchmark/MultiRendererBench.php new file mode 100644 index 000000000..72cf88867 --- /dev/null +++ b/tests/Benchmark/MultiRendererBench.php @@ -0,0 +1,79 @@ +view = (new View($basePath, new SimpleEventDispatcher())) + ->withRenderers([ + 'blade' => new class () implements TemplateRendererInterface { + public function render(ViewInterface $view, string $template, array $parameters): string + { + return file_get_contents($template); + } + }, + 'tpl' => new class () implements TemplateRendererInterface { + public function render(ViewInterface $view, string $template, array $parameters): string + { + return file_get_contents($template); + } + }, + ]); + } + + public function benchRenderPhpView(): void + { + $this->view->render('view'); + } + + public function benchRenderBladeView(): void + { + $this->view->render('view.blade.php'); + } + + public function benchRenderTplView(): void + { + $this->view->render('view.tpl'); + } +} diff --git a/tests/Benchmark/NestedViewsBench.php b/tests/Benchmark/NestedViewsBench.php new file mode 100644 index 000000000..9f6c473b5 --- /dev/null +++ b/tests/Benchmark/NestedViewsBench.php @@ -0,0 +1,51 @@ +render('./sub/sub'); +PHP + ); + } + + $subView = $subDir . '/sub.php'; + if (!is_file($subView)) { + file_put_contents($subView, 'nested-view-content'); + } + + $this->view = new View($basePath, new SimpleEventDispatcher()); + } + + public function benchRenderNestedView(): void + { + $this->view->render('base'); + } +} diff --git a/tests/Benchmark/ViewBasicBench.php b/tests/Benchmark/ViewBasicBench.php new file mode 100644 index 000000000..ae481cc57 --- /dev/null +++ b/tests/Benchmark/ViewBasicBench.php @@ -0,0 +1,25 @@ +view = new View(__DIR__ . '/../public/view', new SimpleEventDispatcher()); + } + + public function benchRenderSimpleView(): void + { + $this->view->render('only-content', [ + 'content' => 'benchmark', + ]); + } +} diff --git a/tests/Benchmark/ViewLocaleBench.php b/tests/Benchmark/ViewLocaleBench.php new file mode 100644 index 000000000..384cc1f96 --- /dev/null +++ b/tests/Benchmark/ViewLocaleBench.php @@ -0,0 +1,32 @@ +view = new View($basePath, new SimpleEventDispatcher()); + $this->view->setLocale('es'); + } + + public function benchRenderLocalizedView(): void + { + $this->view->render('file'); + } +} diff --git a/tests/Benchmark/ViewThemeBench.php b/tests/Benchmark/ViewThemeBench.php new file mode 100644 index 000000000..73831964e --- /dev/null +++ b/tests/Benchmark/ViewThemeBench.php @@ -0,0 +1,44 @@ + $themePath, + ]); + + $this->view = new View($basePath, new SimpleEventDispatcher()); + $this->view->setTheme($theme); + } + + public function benchRenderThemedView(): void + { + $this->view->render('only-content', [ + 'content' => 'benchmark', + ]); + } +} diff --git a/tests/Benchmark/WebViewAssetsBench.php b/tests/Benchmark/WebViewAssetsBench.php new file mode 100644 index 000000000..7765dae7f --- /dev/null +++ b/tests/Benchmark/WebViewAssetsBench.php @@ -0,0 +1,54 @@ +view = new WebView(__DIR__ . '/../public/view', new SimpleEventDispatcher()); + $this->preparedView = $this->view; + } + + #[Bench\BeforeMethods(['benchRegisterAssetsAndRender'])] + public function prepareView(): void + { + $view = $this->view->withClearedState(); + + $view->addCssFiles([ + 'file-1.css', + ['file-2.css', 'crossorigin' => 'anonymous', WebView::POSITION_BEGIN], + ]); + + $view->addJsFiles([ + 'file-1.js', + ['file-2.js', 'async' => true, WebView::POSITION_BEGIN], + ]); + + $view->addCssStrings([ + '.a1 { color: red; }', + ['.a2 { color: red; }', WebView::POSITION_HEAD], + ]); + + $view->addJsStrings([ + 'uniqueName' => 'app1.start();', + 'app2.start();', + ]); + + $this->preparedView = $view; + } + + public function benchRegisterAssetsAndRender(): void + { + $this->preparedView->render('positions.php'); + } +} diff --git a/tests/Benchmark/WebViewBench.php b/tests/Benchmark/WebViewBench.php new file mode 100644 index 000000000..6d321bb39 --- /dev/null +++ b/tests/Benchmark/WebViewBench.php @@ -0,0 +1,26 @@ +view = new WebView(__DIR__ . '/../public/view', new SimpleEventDispatcher()); + $this->view->setTitle('Posts'); + } + + public function benchRenderLayout(): void + { + $this->view->render('layout', [ + 'content' => 'content', + ]); + } +}