From e584171e0ac7a4626d4fdb891bfa5832af042bf6 Mon Sep 17 00:00:00 2001 From: Maxim Akimov Date: Tue, 17 Dec 2024 23:08:29 +0200 Subject: [PATCH 01/13] publish preparation: benchmark --- .gitignore | 3 +- benchmark/.gitignore | 3 + benchmark/benchmark.php | 9 + benchmark/src/Benchmark.php | 260 ++++++++++++++++++++++++++ benchmark/template/template.blade.php | 7 + benchmark/template/template.twig | 7 + 6 files changed, 288 insertions(+), 1 deletion(-) create mode 100644 benchmark/.gitignore create mode 100644 benchmark/benchmark.php create mode 100644 benchmark/src/Benchmark.php create mode 100644 benchmark/template/template.blade.php create mode 100644 benchmark/template/template.twig diff --git a/.gitignore b/.gitignore index 331c58f..8925195 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .idea -vendor \ No newline at end of file +vendor +composer.lock \ No newline at end of file diff --git a/benchmark/.gitignore b/benchmark/.gitignore new file mode 100644 index 0000000..adc675c --- /dev/null +++ b/benchmark/.gitignore @@ -0,0 +1,3 @@ +vendor +composer.lock +tmp \ No newline at end of file diff --git a/benchmark/benchmark.php b/benchmark/benchmark.php new file mode 100644 index 0000000..4e4bb2e --- /dev/null +++ b/benchmark/benchmark.php @@ -0,0 +1,9 @@ +run(); diff --git a/benchmark/src/Benchmark.php b/benchmark/src/Benchmark.php new file mode 100644 index 0000000..084f6a6 --- /dev/null +++ b/benchmark/src/Benchmark.php @@ -0,0 +1,260 @@ +setTemplatesRootPath($phpViewsModelsDir); + $phpViewsNamespace->setTemplateFileExtension('.blade.php'); + $phpViews->addNamespace('Test', $phpViewsNamespace); + + + $twigTemplate = $this->getTestTemplate('.twig'); + $bladeTemplate = $this->getTestTemplate('.blade.php'); + + $templatesCount = 1000; + $templateNameLength = 10; + $itemsInTemplateCount = 100; + + $twigFiles = $this->writeUniqueTemplates( + $twigDir, + '.twig', + $twigTemplate, + $templatesCount, + $templateNameLength, + true + ); + $bladeOriginFiles = $this->writeUniqueTemplates( + $bladeOriginDir, + '.blade.php', + $bladeTemplate, + $templatesCount, + $templateNameLength, + true + ); + $phpViewFiles = $this->writeUniqueTemplates( + $phpViewsDir, + '.blade.php', + $bladeTemplate, + $templatesCount, + $templateNameLength, + false + ); + $phpModelFiles = $this->writeUniqueTemplates( + $phpViewsModelsDir, + '.blade.php', + $bladeTemplate, + $templatesCount, + $templateNameLength, + false + ); + + $templateArguments = [ + 'items' => array_fill(0, $itemsInTemplateCount, 'item'), + ]; + $validationString = sprintf('[%s]', $itemsInTemplateCount); + + $twigSpentMs = $this->measureFileRenders( + 'twig', + function ($templateFile) use ($twig, $templateArguments) { + return $twig->render($templateFile . '.twig', $templateArguments); + }, + $twigFiles, + $validationString + ); + + $bladeOriginSpentMs = $this->measureFileRenders( + 'blade-origin', + function ($templateFile) use ($blade, $templateArguments) { + return $blade->render($templateFile, $templateArguments); + }, + $bladeOriginFiles, + $validationString + ); + + $phpViewsSpentMs = $this->measureFileRenders( + 'php-views', + function ($templateFile) use ($phpViewsRenderer, $templateArguments) { + return $phpViewsRenderer->renderTemplate($templateFile, $templateArguments); + }, + $phpViewFiles, + $validationString + ); + + $phpViewsWithModelsSpentMs = $this->measureFileRenders( + 'php-views', + function ($templateFile) use ($phpViewsRenderer, $templateArguments) { + // fixme + return $phpViewsRenderer->renderTemplate($templateFile, $templateArguments); + }, + $phpViewFiles, + $validationString + ); + + printf("String Renders: %sx\n", $templatesCount); + printf("Twig: %s ms\n", $twigSpentMs); + printf("Blade Origin: %s ms\n", $bladeOriginSpentMs); + printf("PHP Views with built-in Blade: %s ms\n", $phpViewsSpentMs); + printf("PHP Views with built-in Blade and Models: %s ms\n", $phpViewsWithModelsSpentMs); + + $this->removeDir($rootDir); + } + + protected function removeDir(string $directory): void + { + if (false === is_dir($directory)) { + return; + } + + $fsItems = scandir($directory); + + array_walk($fsItems, function ($item) use ($directory) { + if ( + $item === '.' || + $item === '..' + ) { + return; + } + + $path = $directory . DIRECTORY_SEPARATOR . $item; + + if (true === is_dir($path)) { + $this->removeDir($path); + } else { + unlink($path); + } + }); + + rmdir($directory); + } + + protected function writeUniqueTemplates( + string $rootPath, + string $extension, + string $template, + int $count, + int $nameLength, + bool $isNameOnly + ): array { + $randomStrings = $this->getRandomUniqueStrings($count, $nameLength); + + return array_reduce( + $randomStrings, + function ($templateFiles, $randomString) use ($rootPath, $extension, $template, $isNameOnly) { + $templateFile = $rootPath . '/' . $randomString . $extension; + // add name to the content to avoid any potential content-related cache. + $templateContent = $template . ' ' . $randomString; + + file_put_contents($templateFile, $templateContent); + + $templateFiles[] = true === $isNameOnly ? + $randomString : + $templateFile; + + return $templateFiles; + }, + [] + ); + } + + protected function getSpentTimeInMilliseconds(float $start): float + { + $spendTime = (microtime(true) - $start) * 1000; + + return round($spendTime, 2); + } + + protected function measureFileRenders( + string $vendor, + callable $render, + array $templateFiles, + string $validationString + ): float { + $start = microtime(true); + + array_walk($templateFiles, function ($templateFile) use ($vendor, $render, $validationString) { + $rendered = $render($templateFile); + + if (false === strpos($rendered, $validationString)) { + throw new Exception('Unexpected render result for ' . $vendor . ' (' . $templateFile . ')'); + } + }); + + return $this->getSpentTimeInMilliseconds($start); + } + + protected function getRandomUniqueStrings(int $count, int $stringLength, array $uniqueStrings = []): array + { + if (count($uniqueStrings) >= $count) { + return array_keys($uniqueStrings); + } + + $randomString = $this->getRandomString($stringLength); + $uniqueStrings[$randomString] = true; + + return $this->getRandomUniqueStrings($count, $stringLength, $uniqueStrings); + } + + protected function getRandomString(int $length): string + { + $characters = array_merge( + range('0', '9'), + range('a', 'z'), + ); + + $charactersLength = count($characters); + $initialArray = array_fill(0, $length, null); + + $randomLetters = array_map(function () use ($characters, $charactersLength) { + $index = random_int(0, $charactersLength - 1); + + return $characters[$index]; + }, $initialArray); + + return implode('', $randomLetters); + } + + protected function getTestTemplate(string $extension): string + { + $file = __DIR__ . '/../template/template' . $extension; + + if (false === file_exists($file)) { + throw new Exception('Template file not found'); + } + + return (string)file_get_contents($file); + } +} diff --git a/benchmark/template/template.blade.php b/benchmark/template/template.blade.php new file mode 100644 index 0000000..297cc42 --- /dev/null +++ b/benchmark/template/template.blade.php @@ -0,0 +1,7 @@ +@foreach ($items as $item) +1 +@endforeach +[{{ count($items) }}] +@if (count($items) === 100) +1 +@endif \ No newline at end of file diff --git a/benchmark/template/template.twig b/benchmark/template/template.twig new file mode 100644 index 0000000..ed66b64 --- /dev/null +++ b/benchmark/template/template.twig @@ -0,0 +1,7 @@ +{% for item in items %} +1 +{% endfor %} +[{{ items|length }}] +{% if 100 == items|length %} +1 +{% endif %} \ No newline at end of file From abdf4cc0958e4488c0701053e94dbe2542a39e55 Mon Sep 17 00:00:00 2001 From: Maxim Akimov Date: Wed, 18 Dec 2024 00:15:18 +0200 Subject: [PATCH 02/13] publish preparation: benchmark --- benchmark/composer.json | 12 ++ benchmark/src/Benchmark.php | 287 +++++++++++++++++++++++++----------- readme.md | 56 +++++-- 3 files changed, 258 insertions(+), 97 deletions(-) create mode 100644 benchmark/composer.json diff --git a/benchmark/composer.json b/benchmark/composer.json new file mode 100644 index 0000000..2c5a68c --- /dev/null +++ b/benchmark/composer.json @@ -0,0 +1,12 @@ +{ + "require": { + "illuminate/view": "~11.7.0", + "twig/twig": "^3.17", + "jenssegers/blade": "^2.0" + }, + "autoload": { + "psr-4": { + "Benchmark\\": "./src" + } + } +} diff --git a/benchmark/src/Benchmark.php b/benchmark/src/Benchmark.php index 084f6a6..4736273 100644 --- a/benchmark/src/Benchmark.php +++ b/benchmark/src/Benchmark.php @@ -17,120 +17,234 @@ class Benchmark public function run(): void { $rootDir = __DIR__ . '/../tmp'; - $twigDir = $rootDir . '/twig'; - $bladeOriginDir = $rootDir . '/origin-blade'; - $bladeOriginCacheDir = $rootDir . '/origin-blade-cache'; - $phpViewsDir = $rootDir . '/php-views'; - $phpViewsModelsDir = $rootDir . '/php-views-models'; mkdir($rootDir); - mkdir($twigDir); - mkdir($bladeOriginDir); - mkdir($bladeOriginCacheDir); - mkdir($phpViewsDir); - $twigLoader = new FilesystemLoader($twigDir); - $twig = new Environment($twigLoader); - - $blade = new Blade($bladeOriginDir, $bladeOriginCacheDir); - - $phpViewsRenderer = new ViewTemplateRenderer(); - $phpViews=new Views(); - $phpViewsNamespace=new ViewNamespaceConfig($phpViewsRenderer); - $phpViewsNamespace->setTemplatesRootPath($phpViewsModelsDir); - $phpViewsNamespace->setTemplateFileExtension('.blade.php'); - $phpViews->addNamespace('Test', $phpViewsNamespace); - - - $twigTemplate = $this->getTestTemplate('.twig'); - $bladeTemplate = $this->getTestTemplate('.blade.php'); - - $templatesCount = 1000; + $templatesCount = $_SERVER['argc'] > 1 ? + (int)$_SERVER['argv'][1] : + 1000; $templateNameLength = 10; $itemsInTemplateCount = 100; + $templateArguments = [ + 'items' => array_fill(0, $itemsInTemplateCount, 'item'), + ]; + $validationString = sprintf('[%s]', $itemsInTemplateCount); - $twigFiles = $this->writeUniqueTemplates( - $twigDir, - '.twig', - $twigTemplate, + $twigSpentMs = $this->benchmarkForTwig( + $rootDir, + $this->getTestTemplate('.twig'), $templatesCount, $templateNameLength, - true + $templateArguments, + $validationString ); - $bladeOriginFiles = $this->writeUniqueTemplates( - $bladeOriginDir, - '.blade.php', - $bladeTemplate, + + $bladeOriginSpentMs = $this->benchmarkForBladeOrigin( + $rootDir, + $this->getTestTemplate('.blade.php'), $templatesCount, $templateNameLength, - true + $templateArguments, + $validationString ); - $phpViewFiles = $this->writeUniqueTemplates( - $phpViewsDir, - '.blade.php', - $bladeTemplate, + + $phpViewsSpentMs = $this->benchmarkForPhpViews( + $rootDir, + $this->getTestTemplate('.blade.php'), $templatesCount, $templateNameLength, - false + $templateArguments, + $validationString ); - $phpModelFiles = $this->writeUniqueTemplates( - $phpViewsModelsDir, - '.blade.php', - $bladeTemplate, + + $phpViewsWithModelsSpentMs = $this->benchmarkForPhpViewsWithModels( + $rootDir, + $this->getTestTemplate('.blade.php'), $templatesCount, $templateNameLength, - false + $templateArguments, + $validationString ); - $templateArguments = [ - 'items' => array_fill(0, $itemsInTemplateCount, 'item'), + $results = [ + 'Blade from Laravel' => $bladeOriginSpentMs, + 'PHP Views with Models (built-in Blade)' => $phpViewsWithModelsSpentMs, + 'PHP Views without Models (using built-in Blade)' => $phpViewsSpentMs, + 'Twig' => $twigSpentMs, ]; - $validationString = sprintf('[%s]', $itemsInTemplateCount); - $twigSpentMs = $this->measureFileRenders( - 'twig', - function ($templateFile) use ($twig, $templateArguments) { - return $twig->render($templateFile . '.twig', $templateArguments); - }, - $twigFiles, - $validationString + // sort asc + asort($results); + + printf("Renders Count: %sx\n", $templatesCount); + + array_walk($results, function ($contestant, $spentMs) { + printf("%s: %s ms\n", $contestant, $spentMs); + }); + + $this->removeDir($rootDir); + } + + public function benchmarkForBladeOrigin( + string $rootDir, + string $template, + int $templatesCount, + int $templateNameLength, + array $templateArguments, + string $validationString + ): float { + $bladeOriginDir = $rootDir . '/origin-blade'; + $bladeOriginCacheDir = $rootDir . '/origin-blade-cache'; + + mkdir($bladeOriginDir); + mkdir($bladeOriginCacheDir); + + $blade = new Blade($bladeOriginDir, $bladeOriginCacheDir); + + $bladeFiles = $this->writeUniqueTemplates( + $bladeOriginDir, + '.blade.php', + $template, + $templatesCount, + $templateNameLength, + true ); - $bladeOriginSpentMs = $this->measureFileRenders( + return $this->measureFileRenders( 'blade-origin', function ($templateFile) use ($blade, $templateArguments) { return $blade->render($templateFile, $templateArguments); }, - $bladeOriginFiles, + $bladeFiles, $validationString ); + } - $phpViewsSpentMs = $this->measureFileRenders( + protected function benchmarkForPhpViews( + string $rootDir, + string $template, + int $templatesCount, + int $templateNameLength, + array $templateArguments, + string $validationString + ): float { + $phpViewsDir = $rootDir . '/php-views'; + + mkdir($phpViewsDir); + + $phpViewsRenderer = new ViewTemplateRenderer(); + + $phpViewsFiles = $this->writeUniqueTemplates( + $phpViewsDir, + '.blade.php', + $template, + $templatesCount, + $templateNameLength, + false + ); + + return $this->measureFileRenders( 'php-views', function ($templateFile) use ($phpViewsRenderer, $templateArguments) { return $phpViewsRenderer->renderTemplate($templateFile, $templateArguments); }, - $phpViewFiles, + $phpViewsFiles, $validationString ); + } - $phpViewsWithModelsSpentMs = $this->measureFileRenders( - 'php-views', - function ($templateFile) use ($phpViewsRenderer, $templateArguments) { - // fixme - return $phpViewsRenderer->renderTemplate($templateFile, $templateArguments); + protected function benchmarkForPhpViewsWithModels( + string $rootDir, + string $template, + int $templatesCount, + int $templateNameLength, + array $templateArguments, + string $validationString + ): float { + $phpViewsWithModelsDir = $rootDir . '/php-views-with-models'; + + mkdir($phpViewsWithModelsDir); + + $phpViewsRenderer = new ViewTemplateRenderer(); + $phpViews = new Views(); + + $namespaceConfig = new ViewNamespaceConfig($phpViewsRenderer); + $namespaceConfig->setTemplateFileExtension('.blade.php'); + $namespaceConfig->setTemplatesRootPath($phpViewsWithModelsDir); + + $namespace = '_php_views_with_models'; + + $phpViews->addNamespace($namespace, $namespaceConfig); + + $phpViewsWithModelFiles = $this->writeUniqueTemplates( + $phpViewsWithModelsDir, + '.blade.php', + $template, + $templatesCount, + $templateNameLength, + true + ); + + return $this->measureFileRenders( + 'php-views-with-models', + function ($templateFile) use ($namespace, $phpViews, $templateArguments) { + + $modelClass = $this->defineModelClass($namespace, $templateFile); + + return $phpViews->renderModel($modelClass, function ($model) use ($templateArguments) { + $model->items = $templateArguments['items']; + }); }, - $phpViewFiles, + $phpViewsWithModelFiles, $validationString ); + } - printf("String Renders: %sx\n", $templatesCount); - printf("Twig: %s ms\n", $twigSpentMs); - printf("Blade Origin: %s ms\n", $bladeOriginSpentMs); - printf("PHP Views with built-in Blade: %s ms\n", $phpViewsSpentMs); - printf("PHP Views with built-in Blade and Models: %s ms\n", $phpViewsWithModelsSpentMs); + protected function benchmarkForTwig( + string $rootDir, + string $template, + int $templatesCount, + int $templateNameLength, + array $templateArguments, + string $validationString + ): float { + $twigDir = $rootDir . '/twig'; - $this->removeDir($rootDir); + mkdir($twigDir); + + $twigLoader = new FilesystemLoader($twigDir); + $twig = new Environment($twigLoader); + + $twigFiles = $this->writeUniqueTemplates( + $twigDir, + '.twig', + $template, + $templatesCount, + $templateNameLength, + true + ); + + return $this->measureFileRenders( + 'twig', + function ($templateFile) use ($twig, $templateArguments) { + return $twig->render($templateFile . '.twig', $templateArguments); + }, + $twigFiles, + $validationString + ); + } + + protected function defineModelClass(string $namespace, string $className): string + { + $code = sprintf( + 'namespace %s; class %s extends \Prosopo\Views\TemplateModel { public array $items; }', + $namespace, + $className + ); + + eval($code); + + return $namespace . '\\' . $className; } protected function removeDir(string $directory): void @@ -171,23 +285,23 @@ protected function writeUniqueTemplates( ): array { $randomStrings = $this->getRandomUniqueStrings($count, $nameLength); - return array_reduce( - $randomStrings, - function ($templateFiles, $randomString) use ($rootPath, $extension, $template, $isNameOnly) { - $templateFile = $rootPath . '/' . $randomString . $extension; - // add name to the content to avoid any potential content-related cache. - $templateContent = $template . ' ' . $randomString; + return array_reduce( + $randomStrings, + function ($templateFiles, $randomString) use ($rootPath, $extension, $template, $isNameOnly) { + $templateFile = $rootPath . '/' . $randomString . $extension; + // add name to the content to avoid any potential content-related cache. + $templateContent = $template . ' ' . $randomString; - file_put_contents($templateFile, $templateContent); + file_put_contents($templateFile, $templateContent); - $templateFiles[] = true === $isNameOnly ? - $randomString : - $templateFile; + $templateFiles[] = true === $isNameOnly ? + $randomString : + $templateFile; - return $templateFiles; - }, - [] - ); + return $templateFiles; + }, + [] + ); } protected function getSpentTimeInMilliseconds(float $start): float @@ -231,7 +345,6 @@ protected function getRandomUniqueStrings(int $count, int $stringLength, array $ protected function getRandomString(int $length): string { $characters = array_merge( - range('0', '9'), range('a', 'z'), ); diff --git a/readme.md b/readme.md index e381e90..78be11d 100644 --- a/readme.md +++ b/readme.md @@ -5,13 +5,14 @@ built-in [Blade](https://laravel.com/docs/11.x/blade) implementation as a defaul ### Benefits -* Zero Dependencies: Lightweight and easy to integrate into any project. -* Wide Compatibility: PHP 7.4+, 8.0+ -* Adherence to the [SOLID principles](https://en.wikipedia.org/wiki/SOLID): The architecture allows you to easily +* **Blazing fast:** Up to 4x faster than Laravel's Blade and 7x faster than Twig (See + the [Benchmark chapter](#4-benchmark)). +* **Zero Dependencies:** Lightweight and easy to integrate into any project. +* **Wide Compatibility:** PHP 7.4+, 8.0+ +* **Adherence to the [SOLID principles](https://en.wikipedia.org/wiki/SOLID):** The architecture allows you to easily override any module to meet specific requirements. -* Namespace Support: Manage different templates seamlessly under a unified structure. -* Test Coverage: Covered by [Pest](https://pestphp.com/) Unit and Feature tests. -* Static Analysis: Checked by [PHPStan](https://phpstan.org/). +* **Namespace Support**: Manage different templates seamlessly under a unified structure. +* **Reliable**: Covered by [Pest](https://pestphp.com/) tests and checked by [PHPStan](https://phpstan.org/). ### Flexible Usage @@ -28,8 +29,9 @@ You're free to use the package in your own way: - [1. Model-driven approach](#1-model-driven-approach) - [2. Views](#2-views) - [3. View Renderer](#3-view-renderer) -- [4. Contribution](#4-contribution) -- [5. Credits](#5-credits) +- [4. Benchmark](#4-benchmark) +- [5. Contribution](#4-contribution) +- [6. Credits](#5-credits) ## 1. Model-driven approach @@ -503,13 +505,47 @@ $views->addNamespace('MyApp\Models', $viewNamespaceConfig); Now this namespace is configured to deal with plain PHP template files, while having all the package features, including model-driven approach and template error handling. -## 4. Contribution +## 4. Benchmark + +We conducted a [PHP performance benchmark](https://github.com/prosopo/php-views/blob/main/benchmark/src/Benchmark.php) +to compare this package with Laravel's Blade (mocked using [jenssegers/blade](https://github.com/jenssegers/blade)) +and [Twig](https://twig.symfony.com/). Here are the results: + +| Contestant | Renders Count | Spent time, MS | +|----------------------------|---------------|----------------| +| PHP Views (without models) | 1000x | 18.16 | +| PHP Views (with models) | 1000x | 45.1 | +| Blade from Laravel | 1000x | 171.89 | +| Twig | 1000x | 326.33 | + +We used the following package versions: + +* [illuminate/view](https://packagist.org/packages/illuminate/view) `11.7.0` +* [twig/twig](https://packagist.org/packages/twig/twig) `3.17.1` +* [jenssegers/blade](https://packagist.org/packages/jenssegers/blade) `2.0.1` + +> As you can see, even with the model class-related overhead, this package delivers performance that is approximately 4 times +> faster than the Laravel's Blade and 7 times faster than Twig. + +Cache-note: The benchmark is designed to measure raw performance by avoiding any caching mechanisms. + +Since the [benchmark](https://github.com/prosopo/php-views/blob/main/benchmark/src/Benchmark.php) is included in this +repository, you can easily run it locally to verify the results. + +1. `git clone https://github.com/prosopo/php-views.git` +2. `composer install; cd benchmark; composer install` +3. `php benchmark {1000}` - pass your renders count + +We encourage you to enhance the benchmark further - feel free to make it more advanced and submit a pull request. We're +happy to review and accept contributions! ๐Ÿš€ + +## 5. Contribution We would be excited if you decide to contribute! Please read the [for-devs.md](https://github.com/prosopo/php-views/blob/main/for-devs.md) file for project guidelines and agreements. -## 5. Credits +## 6. Credits This package was created by [Maxim Akimov](https://github.com/light-source/) during the development of the [WordPress integration for Prosopo Procaptcha](https://wordpress.org/plugins/prosopo-procaptcha/). From a073ed882b79b1f99effdc30e35cca3707b135a4 Mon Sep 17 00:00:00 2001 From: Maxim Akimov Date: Wed, 18 Dec 2024 00:20:08 +0200 Subject: [PATCH 03/13] publish preparation: benchmark --- readme.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/readme.md b/readme.md index 78be11d..f28234a 100644 --- a/readme.md +++ b/readme.md @@ -1,7 +1,7 @@ # PHP Views Blazing fast PHP Views with model-driven approach, multi-namespace support and -built-in [Blade](https://laravel.com/docs/11.x/blade) implementation as a default template engine. +custom [Blade](https://laravel.com/docs/11.x/blade) implementation as a default template engine. ### Benefits @@ -508,15 +508,15 @@ model-driven approach and template error handling. ## 4. Benchmark We conducted a [PHP performance benchmark](https://github.com/prosopo/php-views/blob/main/benchmark/src/Benchmark.php) -to compare this package with Laravel's Blade (mocked using [jenssegers/blade](https://github.com/jenssegers/blade)) +to compare this package with the Laravel's Blade (mocked using [jenssegers/blade](https://github.com/jenssegers/blade)) and [Twig](https://twig.symfony.com/). Here are the results: -| Contestant | Renders Count | Spent time, MS | -|----------------------------|---------------|----------------| -| PHP Views (without models) | 1000x | 18.16 | -| PHP Views (with models) | 1000x | 45.1 | -| Blade from Laravel | 1000x | 171.89 | -| Twig | 1000x | 326.33 | +| Contestant | Renders Count | Spent time, MS | +|----------------------------------------|---------------|----------------| +| `prosopo/views` (without models) | 1000x | 18.16 | +| `prosopo/views` (with models) | 1000x | 45.1 | +| `illuminate/view` (Blade from Laravel) | 1000x | 171.89 | +| `twig/twig` | 1000x | 326.33 | We used the following package versions: @@ -524,7 +524,8 @@ We used the following package versions: * [twig/twig](https://packagist.org/packages/twig/twig) `3.17.1` * [jenssegers/blade](https://packagist.org/packages/jenssegers/blade) `2.0.1` -> As you can see, even with the model class-related overhead, this package delivers performance that is approximately 4 times +> As you can see, even with the model class-related overhead, this package delivers performance that is approximately 4 +> times > faster than the Laravel's Blade and 7 times faster than Twig. Cache-note: The benchmark is designed to measure raw performance by avoiding any caching mechanisms. From 09cad4820fd60374fd7780305f95447ddc602300 Mon Sep 17 00:00:00 2001 From: Maxim Akimov Date: Wed, 18 Dec 2024 00:23:23 +0200 Subject: [PATCH 04/13] publish preparation: benchmark --- benchmark/src/Benchmark.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmark/src/Benchmark.php b/benchmark/src/Benchmark.php index 4736273..58372fe 100644 --- a/benchmark/src/Benchmark.php +++ b/benchmark/src/Benchmark.php @@ -78,8 +78,8 @@ public function run(): void printf("Renders Count: %sx\n", $templatesCount); - array_walk($results, function ($contestant, $spentMs) { - printf("%s: %s ms\n", $contestant, $spentMs); + array_walk($results, function ($spentMs, $contestant) { + printf("%s ms: %s\n", $spentMs, $contestant); }); $this->removeDir($rootDir); From 93610b0731bc64f9ee3e576ba3a574fb51e89cc8 Mon Sep 17 00:00:00 2001 From: Maxim Akimov Date: Wed, 18 Dec 2024 01:04:09 +0200 Subject: [PATCH 05/13] publish preparation: benchmark --- benchmark/src/Benchmark.php | 27 ++++++++++++++++++--------- readme.md | 23 ++++++++--------------- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/benchmark/src/Benchmark.php b/benchmark/src/Benchmark.php index 58372fe..cc1ae9f 100644 --- a/benchmark/src/Benchmark.php +++ b/benchmark/src/Benchmark.php @@ -30,7 +30,7 @@ public function run(): void ]; $validationString = sprintf('[%s]', $itemsInTemplateCount); - $twigSpentMs = $this->benchmarkForTwig( + list($twigSpentMs, $twigSpentMsWithCache) = $this->benchmarkForTwig( $rootDir, $this->getTestTemplate('.twig'), $templatesCount, @@ -39,7 +39,7 @@ public function run(): void $validationString ); - $bladeOriginSpentMs = $this->benchmarkForBladeOrigin( + list($bladeOriginSpentMs, $bladeOriginSpentMsWithCache) = $this->benchmarkForBladeOrigin( $rootDir, $this->getTestTemplate('.blade.php'), $templatesCount, @@ -67,10 +67,12 @@ public function run(): void ); $results = [ - 'Blade from Laravel' => $bladeOriginSpentMs, + 'Blade from Laravel (raw)' => $bladeOriginSpentMs, + 'Blade from Laravel (with cache)' => $bladeOriginSpentMsWithCache, 'PHP Views with Models (built-in Blade)' => $phpViewsWithModelsSpentMs, 'PHP Views without Models (using built-in Blade)' => $phpViewsSpentMs, - 'Twig' => $twigSpentMs, + 'Twig (raw)' => $twigSpentMs, + 'Twig (with cache)' => $twigSpentMsWithCache, ]; // sort asc @@ -92,7 +94,7 @@ public function benchmarkForBladeOrigin( int $templateNameLength, array $templateArguments, string $validationString - ): float { + ): array { $bladeOriginDir = $rootDir . '/origin-blade'; $bladeOriginCacheDir = $rootDir . '/origin-blade-cache'; @@ -110,7 +112,7 @@ public function benchmarkForBladeOrigin( true ); - return $this->measureFileRenders( + $calc = fn()=> $this->measureFileRenders( 'blade-origin', function ($templateFile) use ($blade, $templateArguments) { return $blade->render($templateFile, $templateArguments); @@ -118,6 +120,8 @@ function ($templateFile) use ($blade, $templateArguments) { $bladeFiles, $validationString ); + + return [$calc(),$calc()]; } protected function benchmarkForPhpViews( @@ -207,13 +211,16 @@ protected function benchmarkForTwig( int $templateNameLength, array $templateArguments, string $validationString - ): float { + ): array { $twigDir = $rootDir . '/twig'; + $twigCacheDir = $rootDir . '/twig-cache'; mkdir($twigDir); $twigLoader = new FilesystemLoader($twigDir); - $twig = new Environment($twigLoader); + $twig = new Environment($twigLoader, [ + 'cache' => $twigCacheDir, + ]); $twigFiles = $this->writeUniqueTemplates( $twigDir, @@ -224,7 +231,7 @@ protected function benchmarkForTwig( true ); - return $this->measureFileRenders( + $calc = fn() => $this->measureFileRenders( 'twig', function ($templateFile) use ($twig, $templateArguments) { return $twig->render($templateFile . '.twig', $templateArguments); @@ -232,6 +239,8 @@ function ($templateFile) use ($twig, $templateArguments) { $twigFiles, $validationString ); + + return [$calc(),$calc()]; } protected function defineModelClass(string $namespace, string $className): string diff --git a/readme.md b/readme.md index f28234a..be93e06 100644 --- a/readme.md +++ b/readme.md @@ -5,8 +5,7 @@ custom [Blade](https://laravel.com/docs/11.x/blade) implementation as a default ### Benefits -* **Blazing fast:** Up to 4x faster than Laravel's Blade and 7x faster than Twig (See - the [Benchmark chapter](#4-benchmark)). +* **Blazing fast:** Outperforms than the origin Laravel's Blade (see the [Benchmark chapter](#4-benchmark)). * **Zero Dependencies:** Lightweight and easy to integrate into any project. * **Wide Compatibility:** PHP 7.4+, 8.0+ * **Adherence to the [SOLID principles](https://en.wikipedia.org/wiki/SOLID):** The architecture allows you to easily @@ -509,14 +508,14 @@ model-driven approach and template error handling. We conducted a [PHP performance benchmark](https://github.com/prosopo/php-views/blob/main/benchmark/src/Benchmark.php) to compare this package with the Laravel's Blade (mocked using [jenssegers/blade](https://github.com/jenssegers/blade)) -and [Twig](https://twig.symfony.com/). Here are the results: +and [Twig](https://twig.symfony.com/). Here are the results for 1000x renders: -| Contestant | Renders Count | Spent time, MS | -|----------------------------------------|---------------|----------------| -| `prosopo/views` (without models) | 1000x | 18.16 | -| `prosopo/views` (with models) | 1000x | 45.1 | -| `illuminate/view` (Blade from Laravel) | 1000x | 171.89 | -| `twig/twig` | 1000x | 326.33 | +| Contestant | First Rendering, MS | Cached Rendering, MS | +|----------------------------------------|---------------------|----------------------| +| `prosopo/views` (without models) | 19.75 | 19.75 (no cache atm) | +| `prosopo/views` (with models) | 43.78 | 43.78 (no cache atm) | +| `illuminate/view` (Blade from Laravel) | 181.24 | 56.77 ms | +| `twig/twig` | 441.13 | 9.47 ms | We used the following package versions: @@ -524,12 +523,6 @@ We used the following package versions: * [twig/twig](https://packagist.org/packages/twig/twig) `3.17.1` * [jenssegers/blade](https://packagist.org/packages/jenssegers/blade) `2.0.1` -> As you can see, even with the model class-related overhead, this package delivers performance that is approximately 4 -> times -> faster than the Laravel's Blade and 7 times faster than Twig. - -Cache-note: The benchmark is designed to measure raw performance by avoiding any caching mechanisms. - Since the [benchmark](https://github.com/prosopo/php-views/blob/main/benchmark/src/Benchmark.php) is included in this repository, you can easily run it locally to verify the results. From 0f3cbe8a9b2ff88181f43ce06087191946e958cf Mon Sep 17 00:00:00 2001 From: Maxim Akimov Date: Wed, 18 Dec 2024 01:05:02 +0200 Subject: [PATCH 06/13] publish preparation: benchmark --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index be93e06..1fe38ec 100644 --- a/readme.md +++ b/readme.md @@ -5,7 +5,7 @@ custom [Blade](https://laravel.com/docs/11.x/blade) implementation as a default ### Benefits -* **Blazing fast:** Outperforms than the origin Laravel's Blade (see the [Benchmark chapter](#4-benchmark)). +* **Blazing fast:** Outperforms the original Laravel Blade (see the [Benchmark chapter](#4-benchmark)). * **Zero Dependencies:** Lightweight and easy to integrate into any project. * **Wide Compatibility:** PHP 7.4+, 8.0+ * **Adherence to the [SOLID principles](https://en.wikipedia.org/wiki/SOLID):** The architecture allows you to easily From 1e3841cb85df22c1ba1664819f4e627af3c228ff Mon Sep 17 00:00:00 2001 From: Maxim Akimov <61589446+light-source@users.noreply.github.com> Date: Wed, 18 Dec 2024 08:58:25 +0200 Subject: [PATCH 07/13] Create LICENSE --- LICENSE | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. From a445b6ead77b530d22ce36a1b5636505406a2fd4 Mon Sep 17 00:00:00 2001 From: Maxim Akimov Date: Wed, 18 Dec 2024 09:56:41 +0200 Subject: [PATCH 08/13] publish preparation: name improvements --- .gitattributes | 6 ++ readme.md => README.md | 24 ++--- benchmark/src/Benchmark.php | 2 +- composer.json | 16 +++ .../CodeRunnerWithErrorHandler.php} | 16 +-- .../CodeRunnerWithGlobalArguments.php} | 14 +-- .../CodeRunnerWithTemplateCompilation.php} | 14 +-- .../PhpCodeRunner.php} | 8 +- private-classes/EventDispatcher.php | 4 +- private-classes/Model/ModelFactory.php | 2 +- .../ModelFactoryWithDefaultsManagement.php | 10 +- ...NameProvider.php => ModelNameResolver.php} | 6 +- ...rovider.php => ModelNamespaceResolver.php} | 6 +- private-classes/Model/ModelRenderer.php | 18 ++-- .../Model/ModelRendererWithEventDetails.php | 12 +-- .../Object/ObjectPropertyWriter.php | 2 +- private-classes/Object/ObjectReader.php | 2 +- .../Object/PropertyValueProviderForModels.php | 2 +- ...ider.php => FileModelTemplateResolver.php} | 30 +++--- private-classes/Template/TemplateRenderer.php | 19 ++-- .../TemplateRendererWithCustomEscape.php | 4 +- .../TemplateRendererWithEventDetails.php | 8 +- .../TemplateRendererWithFileTemplate.php | 4 +- .../TemplateRendererWithModelsRender.php | 4 +- private-classes/View/ViewNamespace.php | 24 ++--- .../View/ViewNamespaceModulesContainer.php | 4 +- ...rInterface.php => CodeRunnerInterface.php} | 4 +- src/Interfaces/EventDispatcherInterface.php | 4 +- .../Model/ModelFactoryInterface.php | 2 +- .../Model/ModelNameProviderInterface.php | 10 -- .../Model/ModelNameResolverInterface.php | 10 ++ ...hp => ModelNamespaceResolverInterface.php} | 4 +- .../Model/ModelRendererInterface.php | 4 +- .../TemplateModelWithDefaultsInterface.php | 2 +- .../Object/ObjectPropertyWriterInterface.php | 5 +- .../Object/ObjectReaderInterface.php | 2 +- ...php => ModelTemplateResolverInterface.php} | 4 +- .../Template/TemplateRendererInterface.php | 2 +- .../View/ViewNamespaceManagerInterface.php | 2 +- ...ViewNamespaceModulesContainerInterface.php | 4 +- src/TemplateModel.php | 4 +- src/View/ViewNamespaceModules.php | 42 ++++---- src/View/ViewTemplateRenderer.php | 20 ++-- src/View/ViewTemplateRendererModules.php | 8 +- src/Views.php | 28 ++--- src/ViewsConfig.php | 8 +- tests/pest/Feature/ViewsTest.php | 54 +++++----- .../CodeRunnerWithErrorHandlerTest.php} | 22 ++-- .../CodeRunnerWithGlobalArgumentsTest.php} | 30 +++--- .../CodeRunnerWithTemplateCompilerTest.php} | 30 +++--- .../PhpCodeRunnerTest.php} | 16 +-- tests/pest/Unit/EventDispatcherTest.php | 8 +- tests/pest/Unit/Model/ModelFactoryTest.php | 6 +- ...ModelFactoryWithDefaultsManagementTest.php | 46 ++++---- .../pest/Unit/Model/ModelNameProviderTest.php | 14 +-- .../Unit/Model/ModelNamespaceProviderTest.php | 14 +-- tests/pest/Unit/Model/ModelRendererTest.php | 22 ++-- .../ModelRendererWithEventDetailsTest.php | 16 +-- .../Unit/Object/ObjectPropertyWriterTest.php | 6 +- tests/pest/Unit/Object/ObjectReaderTest.php | 6 +- .../PropertyValueProviderForModelsTest.php | 2 +- .../FileModelTemplateProviderTest.php | 102 +++++++++--------- .../Unit/Template/TemplateRendererTest.php | 18 ++-- .../TemplateRendererWithEventDetailsTest.php | 12 +-- .../Unit/View/ViewNamespaceContainerTest.php | 14 +-- tests/pest/Unit/View/ViewNamespaceTest.php | 52 ++++----- .../Unit/View/ViewTemplateRendererTest.php | 12 +-- tests/pest/Unit/ViewsTest.php | 4 +- 68 files changed, 473 insertions(+), 463 deletions(-) create mode 100644 .gitattributes rename readme.md => README.md (95%) rename private-classes/{CodeExecutor/CodeExecutorWithErrorHandler.php => CodeRunner/CodeRunnerWithErrorHandler.php} (75%) rename private-classes/{CodeExecutor/CodeExecutorWithGlobalArguments.php => CodeRunner/CodeRunnerWithGlobalArguments.php} (55%) rename private-classes/{CodeExecutor/CodeExecutorWithTemplateCompilation.php => CodeRunner/CodeRunnerWithTemplateCompilation.php} (52%) rename private-classes/{CodeExecutor/PhpCodeExecutor.php => CodeRunner/PhpCodeRunner.php} (59%) rename private-classes/Model/{ModelNameProvider.php => ModelNameResolver.php} (82%) rename private-classes/Model/{ModelNamespaceProvider.php => ModelNamespaceResolver.php} (83%) rename private-classes/Template/{FileModelTemplateProvider.php => FileModelTemplateResolver.php} (70%) rename src/Interfaces/{CodeExecutorInterface.php => CodeRunnerInterface.php} (55%) delete mode 100644 src/Interfaces/Model/ModelNameProviderInterface.php create mode 100644 src/Interfaces/Model/ModelNameResolverInterface.php rename src/Interfaces/Model/{ModelNamespaceProviderInterface.php => ModelNamespaceResolverInterface.php} (64%) rename src/Interfaces/Template/{ModelTemplateProviderInterface.php => ModelTemplateResolverInterface.php} (53%) rename tests/pest/Unit/{CodeExecutor/CodeExecutorWithErrorHandlerTest.php => CodeRunner/CodeRunnerWithErrorHandlerTest.php} (76%) rename tests/pest/Unit/{CodeExecutor/CodeExecutorWithGlobalArgumentsTest.php => CodeRunner/CodeRunnerWithGlobalArgumentsTest.php} (56%) rename tests/pest/Unit/{CodeExecutor/CodeExecutorWithTemplateCompilerTest.php => CodeRunner/CodeRunnerWithTemplateCompilerTest.php} (63%) rename tests/pest/Unit/{CodeExecutor/PhpCodeExecutorTest.php => CodeRunner/PhpCodeRunnerTest.php} (72%) diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..7115125 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +/benchmark export-ignore +/code-quality export-ignore +/tests export-ignore +/.github export-ignore +.gitattributes export-ignore +/.gitignore export-ignore \ No newline at end of file diff --git a/readme.md b/README.md similarity index 95% rename from readme.md rename to README.md index 1fe38ec..3d794e6 100644 --- a/readme.md +++ b/README.md @@ -143,7 +143,7 @@ class AnyClass implements TemplateModelInterface { ## 2. Views -The `Views` class provides the `addNamespace`, `makeModel` and `renderModel` methods. It acts as a +The `Views` class provides the `registerNamespace`, `createModel` and `renderModel` methods. It acts as a namespace manager and brings together different namespace configurations. Each `ViewNamespace` has its own independent setup and set of modules. E.g. among these modules is the @@ -183,7 +183,7 @@ $views = new Views(); // 4. Add the root namespace of your Template Models -$views->addNamespace('MyPackage\Views', $namespaceConfig); +$views->registerNamespace('MyPackage\Views', $namespaceConfig); // Tip: you can have multiple namespaces, and mix their Models. ``` @@ -201,8 +201,6 @@ echo $views->renderModel( $employee->bonus = $bonus; } ); - -// Tip: pass true to the third renderModel() argument to print it without echo. ``` This approach enables a functional programming style when working with Models. @@ -212,7 +210,7 @@ This approach enables a functional programming style when working with Models. When you need split creation, use the factory to create the model, and then render later when you need it. ```php -$employee = $views->makeModel(EmployeeModel::class); +$employee = $views->createModel(EmployeeModel::class); // ... @@ -227,9 +225,9 @@ echo $views->renderModel($employee); // to customize the Model properties before rendering. ``` -Advice: The `Views` class implements three interfaces: `ViewNamespaceManagerInterface` (for `addNamespace`), +Advice: The `Views` class implements three interfaces: `ViewNamespaceManagerInterface` (for `registerNamespace`), `ModelFactoryInterface` (for -`makeModel`), and `ModelRendererInterface` (for `renderModel`). +`createModel`), and `ModelRendererInterface` (for `renderModel`). When passing the `Views` instance to your methods, use one of these interfaces as the argument type instead of the `Views` class itself. @@ -239,7 +237,7 @@ you expect are accessible, promoting cleaner and more maintainable code. ### 2.4) Automated templates matching -The built-in `ModelTemplateProvider` automatically matches templates based on the Model names and their relative +The built-in `ModelTemplateResolver` automatically matches templates based on the Model names and their relative namespaces. This automates the process of associating templates with their corresponding Models. Example: @@ -257,13 +255,13 @@ Example: **Naming Note:** Use dashes in template names, as camelCase in Model names is automatically converted to dash-separated names. -> Tip: In case this approach doesn't work for your setup, you can override the `ModelTemplateProvider` module to -> implement your own logic. In case the reason is the name-specific only, consider overriding the `ModelNameProvider` +> Tip: In case this approach doesn't work for your setup, you can override the `ModelTemplateResolver` module to +> implement your own logic. In case the reason is the name-specific only, consider overriding the `ModelNameResolver` > module instead. ### 2.5) Custom modules -By default, the `addNamespace` class creates module instances for the namespace using classes from the current package. +By default, the `registerNamespace` class creates module instances for the namespace using classes from the current package. If you need to override the default module behavior, you can define a custom implementation in the configuration and the package will use the specified implementation. @@ -308,7 +306,7 @@ $views = new Views(); // 4. Add the namespace (you can have multiple namespaces) -$views->addNamespace('MyPackage\Views', $namespaceConfig); +$views->registerNamespace('MyPackage\Views', $namespaceConfig); ``` You can override any namespace module in the following way: @@ -498,7 +496,7 @@ $viewNamespaceConfig ->setTemplatesRootPath(__DIR__ . './templates') ->setTemplateFileExtension('.php'); -$views->addNamespace('MyApp\Models', $viewNamespaceConfig); +$views->registerNamespace('MyApp\Models', $viewNamespaceConfig); ``` Now this namespace is configured to deal with plain PHP template files, while having all the package features, including diff --git a/benchmark/src/Benchmark.php b/benchmark/src/Benchmark.php index cc1ae9f..2739ee7 100644 --- a/benchmark/src/Benchmark.php +++ b/benchmark/src/Benchmark.php @@ -178,7 +178,7 @@ protected function benchmarkForPhpViewsWithModels( $namespace = '_php_views_with_models'; - $phpViews->addNamespace($namespace, $namespaceConfig); + $phpViews->registerNamespace($namespace, $namespaceConfig); $phpViewsWithModelFiles = $this->writeUniqueTemplates( $phpViewsWithModelsDir, diff --git a/composer.json b/composer.json index 531a184..d74ffef 100644 --- a/composer.json +++ b/composer.json @@ -1,4 +1,20 @@ { + "name": "prosopo/views", + "description": "Blazing fast Views with model-driven approach, multi-namespace support and custom Blade implementation as a default template engine.", + "keywords": [ + "Views", + "templates", + "models", + "Blade", + "Twig" + ], + "license": "Apache-2.0", + "authors": [ + { + "name": "Maxim Akimov", + "homepage": "https://github.com/light-source/" + } + ], "require": { "php": "^7.4|^8.0" }, diff --git a/private-classes/CodeExecutor/CodeExecutorWithErrorHandler.php b/private-classes/CodeRunner/CodeRunnerWithErrorHandler.php similarity index 75% rename from private-classes/CodeExecutor/CodeExecutorWithErrorHandler.php rename to private-classes/CodeRunner/CodeRunnerWithErrorHandler.php index bae0715..5c4368a 100644 --- a/private-classes/CodeExecutor/CodeExecutorWithErrorHandler.php +++ b/private-classes/CodeRunner/CodeRunnerWithErrorHandler.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace Prosopo\Views\PrivateClasses\CodeExecutor; +namespace Prosopo\Views\PrivateClasses\CodeRunner; use ErrorException; -use Prosopo\Views\Interfaces\CodeExecutorInterface; +use Prosopo\Views\Interfaces\CodeRunnerInterface; use Prosopo\Views\Interfaces\EventDispatcherInterface; use Throwable; @@ -13,23 +13,23 @@ * This class is marked as a final and placed under the 'Private' namespace to prevent anyone from using it directly. * We reserve the right to change its name and implementation. */ -final class CodeExecutorWithErrorHandler implements CodeExecutorInterface +final class CodeRunnerWithErrorHandler implements CodeRunnerInterface { - private CodeExecutorInterface $codeExecutor; + private CodeRunnerInterface $codeExecutor; private EventDispatcherInterface $eventDispatcher; private string $errorEventName; public function __construct( - CodeExecutorInterface $codeExecutor, + CodeRunnerInterface $codeExecutor, EventDispatcherInterface $eventDispatcher, - string $errorEventName + string $errorEventName ) { $this->codeExecutor = $codeExecutor; $this->eventDispatcher = $eventDispatcher; $this->errorEventName = $errorEventName; } - public function executeCode(string $code, array $arguments = []): void + public function runCode(string $code, array $arguments = []): void { $errorDetails = [ 'arguments' => $arguments, @@ -43,7 +43,7 @@ public function executeCode(string $code, array $arguments = []): void throw new ErrorException($message, 0, $severity, $file, $line); }); - $this->codeExecutor->executeCode($code, $arguments); + $this->codeExecutor->runCode($code, $arguments); } catch (Throwable $error) { $errorDetails['error'] = $error; diff --git a/private-classes/CodeExecutor/CodeExecutorWithGlobalArguments.php b/private-classes/CodeRunner/CodeRunnerWithGlobalArguments.php similarity index 55% rename from private-classes/CodeExecutor/CodeExecutorWithGlobalArguments.php rename to private-classes/CodeRunner/CodeRunnerWithGlobalArguments.php index d85f4aa..46570c1 100644 --- a/private-classes/CodeExecutor/CodeExecutorWithGlobalArguments.php +++ b/private-classes/CodeRunner/CodeRunnerWithGlobalArguments.php @@ -2,17 +2,17 @@ declare(strict_types=1); -namespace Prosopo\Views\PrivateClasses\CodeExecutor; +namespace Prosopo\Views\PrivateClasses\CodeRunner; -use Prosopo\Views\Interfaces\CodeExecutorInterface; +use Prosopo\Views\Interfaces\CodeRunnerInterface; /** * This class is marked as a final and placed under the 'Private' namespace to prevent anyone from using it directly. * We reserve the right to change its name and implementation. */ -final class CodeExecutorWithGlobalArguments implements CodeExecutorInterface +final class CodeRunnerWithGlobalArguments implements CodeRunnerInterface { - private CodeExecutorInterface $codeExecutor; + private CodeRunnerInterface $codeExecutor; /** * @var array */ @@ -21,16 +21,16 @@ final class CodeExecutorWithGlobalArguments implements CodeExecutorInterface /** * @param array $globalArguments */ - public function __construct(CodeExecutorInterface $codeExecutor, array $globalArguments) + public function __construct(CodeRunnerInterface $codeExecutor, array $globalArguments) { $this->codeExecutor = $codeExecutor; $this->globalArguments = $globalArguments; } - public function executeCode(string $code, array $arguments = []): void + public function runCode(string $code, array $arguments = []): void { $arguments = array_merge($this->globalArguments, $arguments); - $this->codeExecutor->executeCode($code, $arguments); + $this->codeExecutor->runCode($code, $arguments); } } diff --git a/private-classes/CodeExecutor/CodeExecutorWithTemplateCompilation.php b/private-classes/CodeRunner/CodeRunnerWithTemplateCompilation.php similarity index 52% rename from private-classes/CodeExecutor/CodeExecutorWithTemplateCompilation.php rename to private-classes/CodeRunner/CodeRunnerWithTemplateCompilation.php index b285adf..99791f9 100644 --- a/private-classes/CodeExecutor/CodeExecutorWithTemplateCompilation.php +++ b/private-classes/CodeRunner/CodeRunnerWithTemplateCompilation.php @@ -2,30 +2,30 @@ declare(strict_types=1); -namespace Prosopo\Views\PrivateClasses\CodeExecutor; +namespace Prosopo\Views\PrivateClasses\CodeRunner; -use Prosopo\Views\Interfaces\CodeExecutorInterface; +use Prosopo\Views\Interfaces\CodeRunnerInterface; use Prosopo\Views\Interfaces\Template\TemplateCompilerInterface; /** * This class is marked as a final and placed under the 'Private' namespace to prevent anyone from using it directly. * We reserve the right to change its name and implementation. */ -final class CodeExecutorWithTemplateCompilation implements CodeExecutorInterface +final class CodeRunnerWithTemplateCompilation implements CodeRunnerInterface { - private CodeExecutorInterface $codeExecutor; + private CodeRunnerInterface $codeExecutor; private TemplateCompilerInterface $templateCompiler; - public function __construct(CodeExecutorInterface $codeExecutor, TemplateCompilerInterface $templateCompiler) + public function __construct(CodeRunnerInterface $codeExecutor, TemplateCompilerInterface $templateCompiler) { $this->codeExecutor = $codeExecutor; $this->templateCompiler = $templateCompiler; } - public function executeCode(string $code, array $arguments = []): void + public function runCode(string $code, array $arguments = []): void { $compiledCode = $this->templateCompiler->compileTemplate($code); - $this->codeExecutor->executeCode($compiledCode, $arguments); + $this->codeExecutor->runCode($compiledCode, $arguments); } } diff --git a/private-classes/CodeExecutor/PhpCodeExecutor.php b/private-classes/CodeRunner/PhpCodeRunner.php similarity index 59% rename from private-classes/CodeExecutor/PhpCodeExecutor.php rename to private-classes/CodeRunner/PhpCodeRunner.php index b4cc2b2..0d19879 100644 --- a/private-classes/CodeExecutor/PhpCodeExecutor.php +++ b/private-classes/CodeRunner/PhpCodeRunner.php @@ -2,17 +2,17 @@ declare(strict_types=1); -namespace Prosopo\Views\PrivateClasses\CodeExecutor; +namespace Prosopo\Views\PrivateClasses\CodeRunner; -use Prosopo\Views\Interfaces\CodeExecutorInterface; +use Prosopo\Views\Interfaces\CodeRunnerInterface; /** * This class is marked as a final and placed under the 'Private' namespace to prevent anyone from using it directly. * We reserve the right to change its name and implementation. */ -final class PhpCodeExecutor implements CodeExecutorInterface +final class PhpCodeRunner implements CodeRunnerInterface { - public function executeCode(string $code, array $arguments = []): void + public function runCode(string $code, array $arguments = []): void { // @phpcs:ignore extract($arguments); diff --git a/private-classes/EventDispatcher.php b/private-classes/EventDispatcher.php index 3c92a06..4c78b38 100644 --- a/private-classes/EventDispatcher.php +++ b/private-classes/EventDispatcher.php @@ -64,14 +64,14 @@ public function removeEventListener(string $eventName, callable $eventListener): $this->eventListeners[$eventName] = $eventListeners; } - public function attachEventDetails(string $eventName, array $eventDetails): void + public function registerEventDetails(string $eventName, array $eventDetails): void { $eventDetails = array_merge($this->getEventDetails($eventName), $eventDetails); $this->eventDetails[$eventName] = $eventDetails; } - public function detachEventDetails(string $eventName, array $eventDetails): void + public function unregisterEventDetails(string $eventName, array $eventDetails): void { $eventDetails = array_diff_key($this->getEventDetails($eventName), $eventDetails); diff --git a/private-classes/Model/ModelFactory.php b/private-classes/Model/ModelFactory.php index 39eecdf..3802e0c 100644 --- a/private-classes/Model/ModelFactory.php +++ b/private-classes/Model/ModelFactory.php @@ -25,7 +25,7 @@ public function __construct( $this->propertyValueProvider = $propertyValueProvider; } - public function makeModel(string $modelClass) + public function createModel(string $modelClass) { return new $modelClass($this->objectReader, $this->propertyValueProvider); } diff --git a/private-classes/Model/ModelFactoryWithDefaultsManagement.php b/private-classes/Model/ModelFactoryWithDefaultsManagement.php index e325560..7669436 100644 --- a/private-classes/Model/ModelFactoryWithDefaultsManagement.php +++ b/private-classes/Model/ModelFactoryWithDefaultsManagement.php @@ -29,9 +29,9 @@ public function __construct( $this->objectPropertyWriter = $objectPropertyWriter; } - public function makeModel(string $modelClass) + public function createModel(string $modelClass) { - $model = $this->modelFactory->makeModel($modelClass); + $model = $this->modelFactory->createModel($modelClass); if (true === ($model instanceof TemplateModelWithDefaultsInterface)) { $this->setDefaultValuesRecursively($model); @@ -42,12 +42,12 @@ public function makeModel(string $modelClass) protected function setDefaultValuesRecursively(TemplateModelWithDefaultsInterface $modelWithDefaults): void { - $defaultsPropertyValueProvider = $modelWithDefaults->getDefaultsPropertyValueProvider(); + $defaultsPropertyValueProvider = $modelWithDefaults->getDefaultsProvider(); - $this->objectPropertyWriter->setObjectPropertyValues($modelWithDefaults, $defaultsPropertyValueProvider); + $this->objectPropertyWriter->assignPropertyValues($modelWithDefaults, $defaultsPropertyValueProvider); $innerModelsWithDefaults = $this->getInnerModels( - $this->objectPropertyReader->getObjectVariables($modelWithDefaults) + $this->objectPropertyReader->extractObjectVariables($modelWithDefaults) ); array_map(function (TemplateModelWithDefaultsInterface $innerModelWithDefaults) { diff --git a/private-classes/Model/ModelNameProvider.php b/private-classes/Model/ModelNameResolver.php similarity index 82% rename from private-classes/Model/ModelNameProvider.php rename to private-classes/Model/ModelNameResolver.php index 5d3f703..f833073 100644 --- a/private-classes/Model/ModelNameProvider.php +++ b/private-classes/Model/ModelNameResolver.php @@ -4,7 +4,7 @@ namespace Prosopo\Views\PrivateClasses\Model; -use Prosopo\Views\Interfaces\Model\ModelNameProviderInterface; +use Prosopo\Views\Interfaces\Model\ModelNameResolverInterface; use Prosopo\Views\Interfaces\Model\TemplateModelInterface; use Prosopo\Views\PrivateClasses\Object\ObjectClassReader; @@ -12,7 +12,7 @@ * This class is marked as a final and placed under the 'Private' namespace to prevent anyone from using it directly. * We reserve the right to change its name and implementation. */ -final class ModelNameProvider implements ModelNameProviderInterface +final class ModelNameResolver implements ModelNameResolverInterface { private ObjectClassReader $objectClassReader; @@ -21,7 +21,7 @@ public function __construct(ObjectClassReader $objectClassReader) $this->objectClassReader = $objectClassReader; } - public function getModelName(TemplateModelInterface $model): string + public function resolveModelName(TemplateModelInterface $model): string { $modelNamespaceWithClassName = $this->objectClassReader->getObjectClass($model) ; diff --git a/private-classes/Model/ModelNamespaceProvider.php b/private-classes/Model/ModelNamespaceResolver.php similarity index 83% rename from private-classes/Model/ModelNamespaceProvider.php rename to private-classes/Model/ModelNamespaceResolver.php index 2a5d0ac..0ecdfea 100644 --- a/private-classes/Model/ModelNamespaceProvider.php +++ b/private-classes/Model/ModelNamespaceResolver.php @@ -4,14 +4,14 @@ namespace Prosopo\Views\PrivateClasses\Model; -use Prosopo\Views\Interfaces\Model\ModelNamespaceProviderInterface; +use Prosopo\Views\Interfaces\Model\ModelNamespaceResolverInterface; use Prosopo\Views\PrivateClasses\Object\ObjectClassReader; /** * This class is marked as a final and placed under the 'Private' namespace to prevent anyone from using it directly. * We reserve the right to change its name and implementation. */ -final class ModelNamespaceProvider implements ModelNamespaceProviderInterface +final class ModelNamespaceResolver implements ModelNamespaceResolverInterface { private ObjectClassReader $objectClassReader; @@ -20,7 +20,7 @@ public function __construct(ObjectClassReader $objectClassReader) $this->objectClassReader = $objectClassReader; } - public function getModelNamespace($modelOrClass): string + public function resolveModelNamespace($modelOrClass): string { $modelNamespaceWithClassName = false === is_string($modelOrClass) ? $this->objectClassReader->getObjectClass($modelOrClass) : diff --git a/private-classes/Model/ModelRenderer.php b/private-classes/Model/ModelRenderer.php index d35d0f3..12af6e0 100644 --- a/private-classes/Model/ModelRenderer.php +++ b/private-classes/Model/ModelRenderer.php @@ -7,7 +7,7 @@ use Closure; use Prosopo\Views\Interfaces\Model\ModelFactoryInterface; use Prosopo\Views\Interfaces\Model\ModelRendererInterface; -use Prosopo\Views\Interfaces\Template\ModelTemplateProviderInterface; +use Prosopo\Views\Interfaces\Template\ModelTemplateResolverInterface; use Prosopo\Views\Interfaces\Template\TemplateRendererInterface; /** @@ -18,31 +18,31 @@ final class ModelRenderer implements ModelRendererInterface { private TemplateRendererInterface $templateRenderer; private ModelFactoryInterface $viewFactory; - private ModelTemplateProviderInterface $templateProvider; + private ModelTemplateResolverInterface $templateProvider; public function __construct( TemplateRendererInterface $templateRenderer, ModelFactoryInterface $modelFactory, - ModelTemplateProviderInterface $templateProvider + ModelTemplateResolverInterface $templateProvider ) { $this->templateRenderer = $templateRenderer; $this->viewFactory = $modelFactory; $this->templateProvider = $templateProvider; } - public function renderModel($modelOrClass, ?Closure $setupCallback = null, bool $doPrint = false): string + public function renderModel($modelOrClass, ?Closure $setupModelCallback = null): string { $model = true === is_string($modelOrClass) ? - $this->viewFactory->makeModel($modelOrClass) : + $this->viewFactory->createModel($modelOrClass) : $modelOrClass; - if (null !== $setupCallback) { - $setupCallback($model); + if (null !== $setupModelCallback) { + $setupModelCallback($model); } $variables = $model->getTemplateArguments(); - $template = $this->templateProvider->getModelTemplate($model); + $template = $this->templateProvider->resolveModelTemplate($model); - return $this->templateRenderer->renderTemplate($template, $variables, $doPrint); + return $this->templateRenderer->renderTemplate($template, $variables); } } diff --git a/private-classes/Model/ModelRendererWithEventDetails.php b/private-classes/Model/ModelRendererWithEventDetails.php index c21268a..fff5a3e 100644 --- a/private-classes/Model/ModelRendererWithEventDetails.php +++ b/private-classes/Model/ModelRendererWithEventDetails.php @@ -28,21 +28,21 @@ public function __construct( $this->eventName = $eventName; } - public function renderModel($modelOrClass, Closure $setupCallback = null, bool $doPrint = false): string + public function renderModel($modelOrClass, Closure $setupModelCallback = null): string { - $viewClass = true === is_string($modelOrClass) ? + $modelClass = true === is_string($modelOrClass) ? $modelOrClass : get_class($modelOrClass); $eventDetails = [ - 'viewClass' => $viewClass, + 'modelClass' => $modelClass, ]; - $this->eventDispatcher->attachEventDetails($this->eventName, $eventDetails); + $this->eventDispatcher->registerEventDetails($this->eventName, $eventDetails); - $response = $this->viewRenderer->renderModel($modelOrClass, $setupCallback, $doPrint); + $response = $this->viewRenderer->renderModel($modelOrClass, $setupModelCallback); - $this->eventDispatcher->detachEventDetails($this->eventName, $eventDetails); + $this->eventDispatcher->unregisterEventDetails($this->eventName, $eventDetails); return $response; } diff --git a/private-classes/Object/ObjectPropertyWriter.php b/private-classes/Object/ObjectPropertyWriter.php index 1f7fc82..8d003e8 100644 --- a/private-classes/Object/ObjectPropertyWriter.php +++ b/private-classes/Object/ObjectPropertyWriter.php @@ -15,7 +15,7 @@ */ final class ObjectPropertyWriter implements ObjectPropertyWriterInterface { - public function setObjectPropertyValues( + public function assignPropertyValues( object $instance, PropertyValueProviderInterface $propertyValueProvider ): void { diff --git a/private-classes/Object/ObjectReader.php b/private-classes/Object/ObjectReader.php index ec1fc67..3440c77 100644 --- a/private-classes/Object/ObjectReader.php +++ b/private-classes/Object/ObjectReader.php @@ -15,7 +15,7 @@ */ final class ObjectReader implements ObjectReaderInterface { - public function getObjectVariables(object $instance): array + public function extractObjectVariables(object $instance): array { $reflectionClass = new ReflectionClass($instance); diff --git a/private-classes/Object/PropertyValueProviderForModels.php b/private-classes/Object/PropertyValueProviderForModels.php index 4a1e80a..8792550 100644 --- a/private-classes/Object/PropertyValueProviderForModels.php +++ b/private-classes/Object/PropertyValueProviderForModels.php @@ -47,7 +47,7 @@ public function getPropertyValue(ReflectionProperty $property) $modelClassString = $this->getModelClassStringForInheritors($type); return null !== $modelClassString ? - $this->modelFactory->makeModel($modelClassString) : + $this->modelFactory->createModel($modelClassString) : null; } diff --git a/private-classes/Template/FileModelTemplateProvider.php b/private-classes/Template/FileModelTemplateResolver.php similarity index 70% rename from private-classes/Template/FileModelTemplateProvider.php rename to private-classes/Template/FileModelTemplateResolver.php index 9cdccbf..c861fba 100644 --- a/private-classes/Template/FileModelTemplateProvider.php +++ b/private-classes/Template/FileModelTemplateResolver.php @@ -4,31 +4,31 @@ namespace Prosopo\Views\PrivateClasses\Template; -use Prosopo\Views\Interfaces\Model\ModelNameProviderInterface; -use Prosopo\Views\Interfaces\Model\ModelNamespaceProviderInterface; +use Prosopo\Views\Interfaces\Model\ModelNameResolverInterface; +use Prosopo\Views\Interfaces\Model\ModelNamespaceResolverInterface; use Prosopo\Views\Interfaces\Model\TemplateModelInterface; -use Prosopo\Views\Interfaces\Template\ModelTemplateProviderInterface; +use Prosopo\Views\Interfaces\Template\ModelTemplateResolverInterface; /** * This class is marked as a final and placed under the 'Private' namespace to prevent anyone from using it directly. * We reserve the right to change its name and implementation. */ -final class FileModelTemplateProvider implements ModelTemplateProviderInterface +final class FileModelTemplateResolver implements ModelTemplateResolverInterface { private string $templatesRootPath; private string $namespace; private string $extension; private bool $isFileBasedTemplate; - private ModelNameProviderInterface $modelNameProvider; - private ModelNamespaceProviderInterface $modelNamespaceProvider; + private ModelNameResolverInterface $modelNameProvider; + private ModelNamespaceResolverInterface $modelNamespaceProvider; public function __construct( - string $namespace, - string $templatesRootPath, - string $extension, - bool $isFileBasedTemplate, - ModelNamespaceProviderInterface $modelNamespaceProvider, - ModelNameProviderInterface $modelNameProvider + string $namespace, + string $templatesRootPath, + string $extension, + bool $isFileBasedTemplate, + ModelNamespaceResolverInterface $modelNamespaceProvider, + ModelNameResolverInterface $modelNameProvider ) { $this->templatesRootPath = $templatesRootPath; $this->namespace = $namespace; @@ -38,14 +38,14 @@ public function __construct( $this->modelNamespaceProvider = $modelNamespaceProvider; } - public function getModelTemplate(TemplateModelInterface $model): string + public function resolveModelTemplate(TemplateModelInterface $model): string { - $modelNamespace = $this->modelNamespaceProvider->getModelNamespace($model); + $modelNamespace = $this->modelNamespaceProvider->resolveModelNamespace($model); $relativeModelNamespace = substr($modelNamespace, strlen($this->namespace)); $relativeModelNamespace = ltrim($relativeModelNamespace, '\\'); - $modelName = $this->modelNameProvider->getModelName($model); + $modelName = $this->modelNameProvider->resolveModelName($model); $relativeTemplatePath = $this->getRelativeTemplatePath($relativeModelNamespace, $modelName); diff --git a/private-classes/Template/TemplateRenderer.php b/private-classes/Template/TemplateRenderer.php index 3df0dea..d1b2627 100644 --- a/private-classes/Template/TemplateRenderer.php +++ b/private-classes/Template/TemplateRenderer.php @@ -4,7 +4,7 @@ namespace Prosopo\Views\PrivateClasses\Template; -use Prosopo\Views\Interfaces\CodeExecutorInterface; +use Prosopo\Views\Interfaces\CodeRunnerInterface; use Prosopo\Views\Interfaces\Template\TemplateRendererInterface; /** @@ -13,26 +13,19 @@ */ final class TemplateRenderer implements TemplateRendererInterface { - private CodeExecutorInterface $codeExecutor; + private CodeRunnerInterface $codeExecutor; - public function __construct(CodeExecutorInterface $codeExecutor) + public function __construct(CodeRunnerInterface $codeExecutor) { $this->codeExecutor = $codeExecutor; } - public function renderTemplate(string $template, array $variables = [], bool $doPrint = false): string + public function renderTemplate(string $template, array $variables = []): string { ob_start(); - $this->codeExecutor->executeCode($template, $variables); + $this->codeExecutor->runCode($template, $variables); - $html = (string)ob_get_clean(); - - if (true === $doPrint) { - // @phpcs:ignore - echo $html; - } - - return $html; + return (string)ob_get_clean(); } } diff --git a/private-classes/Template/TemplateRendererWithCustomEscape.php b/private-classes/Template/TemplateRendererWithCustomEscape.php index 1dfe821..8c50f0e 100644 --- a/private-classes/Template/TemplateRendererWithCustomEscape.php +++ b/private-classes/Template/TemplateRendererWithCustomEscape.php @@ -32,7 +32,7 @@ public function __construct( $this->escapeVariableName = $escapeVariableName; } - public function renderTemplate(string $template, array $variables = [], bool $doPrint = false): string + public function renderTemplate(string $template, array $variables = []): string { $variables = $this->setOutputEscapeCallback( $variables, @@ -40,7 +40,7 @@ public function renderTemplate(string $template, array $variables = [], bool $do $this->customOutputEscapeCallback ); - return $this->templateRenderer->renderTemplate($template, $variables, $doPrint); + return $this->templateRenderer->renderTemplate($template, $variables); } /** diff --git a/private-classes/Template/TemplateRendererWithEventDetails.php b/private-classes/Template/TemplateRendererWithEventDetails.php index 30af8a8..f3e4075 100644 --- a/private-classes/Template/TemplateRendererWithEventDetails.php +++ b/private-classes/Template/TemplateRendererWithEventDetails.php @@ -27,17 +27,17 @@ public function __construct( $this->eventName = $eventName; } - public function renderTemplate(string $template, array $variables = [], bool $doPrint = false): string + public function renderTemplate(string $template, array $variables = []): string { $eventDetails = [ 'template' => $template, ]; - $this->eventDispatcher->attachEventDetails($this->eventName, $eventDetails); + $this->eventDispatcher->registerEventDetails($this->eventName, $eventDetails); - $response = $this->templateRenderer->renderTemplate($template, $variables, $doPrint); + $response = $this->templateRenderer->renderTemplate($template, $variables); - $this->eventDispatcher->detachEventDetails($this->eventName, $eventDetails); + $this->eventDispatcher->unregisterEventDetails($this->eventName, $eventDetails); return $response; } diff --git a/private-classes/Template/TemplateRendererWithFileTemplate.php b/private-classes/Template/TemplateRendererWithFileTemplate.php index 29bd30e..8d70e43 100644 --- a/private-classes/Template/TemplateRendererWithFileTemplate.php +++ b/private-classes/Template/TemplateRendererWithFileTemplate.php @@ -19,11 +19,11 @@ public function __construct(TemplateRendererInterface $templateRenderer) $this->templateRenderer = $templateRenderer; } - public function renderTemplate(string $template, array $variables = [], bool $doPrint = false): string + public function renderTemplate(string $template, array $variables = []): string { $template = $this->getFileContent($template); - return $this->templateRenderer->renderTemplate($template, $variables, $doPrint); + return $this->templateRenderer->renderTemplate($template, $variables); } protected function getFileContent(string $file): string diff --git a/private-classes/Template/TemplateRendererWithModelsRender.php b/private-classes/Template/TemplateRendererWithModelsRender.php index f3e4002..b18c0b1 100644 --- a/private-classes/Template/TemplateRendererWithModelsRender.php +++ b/private-classes/Template/TemplateRendererWithModelsRender.php @@ -24,11 +24,11 @@ public function __construct(TemplateRendererInterface $templateRenderer, ModelRe $this->modelRenderer = $modelRenderer; } - public function renderTemplate(string $template, array $variables = [], bool $doPrint = false): string + public function renderTemplate(string $template, array $variables = []): string { $variables = $this->renderModels($variables); - return $this->templateRenderer->renderTemplate($template, $variables, $doPrint); + return $this->templateRenderer->renderTemplate($template, $variables); } /** diff --git a/private-classes/View/ViewNamespace.php b/private-classes/View/ViewNamespace.php index 51d3659..2e6e08c 100644 --- a/private-classes/View/ViewNamespace.php +++ b/private-classes/View/ViewNamespace.php @@ -16,12 +16,12 @@ PropertyValueProviderForNullable}; use Prosopo\Views\PrivateClasses\Model\{ModelFactory, ModelFactoryWithDefaultsManagement, - ModelNameProvider, - ModelNamespaceProvider, + ModelNameResolver, + ModelNamespaceResolver, ModelRenderer, ModelRendererWithEventDetails}; use Prosopo\Views\PrivateClasses\EventDispatcher; -use Prosopo\Views\PrivateClasses\Template\FileModelTemplateProvider; +use Prosopo\Views\PrivateClasses\Template\FileModelTemplateResolver; use Prosopo\Views\PrivateClasses\Template\TemplateRendererWithModelsRender; use Prosopo\Views\View\ViewNamespaceConfig; use Prosopo\Views\View\ViewNamespaceModules; @@ -71,19 +71,19 @@ public function __construct( new ObjectPropertyWriter() : $objectPropertyWriter; - $modelNamespaceProvider = $modules->getModelNamespaceProvider(); + $modelNamespaceProvider = $modules->getModelNamespaceResolver(); $modelNamespaceProvider = null === $modelNamespaceProvider ? - new ModelNamespaceProvider(new ObjectClassReader()) : + new ModelNamespaceResolver(new ObjectClassReader()) : $modelNamespaceProvider; - $modelNameProvider = $modules->getModelNameProvider(); + $modelNameProvider = $modules->getModelNameResolver(); $modelNameProvider = null === $modelNameProvider ? - new ModelNameProvider(new ObjectClassReader()) : + new ModelNameResolver(new ObjectClassReader()) : $modelNameProvider; - $templateProvider = $modules->getModelTemplateProvider(); + $templateProvider = $modules->getModelTemplateResolver(); $templateProvider = null === $templateProvider ? - new FileModelTemplateProvider( + new FileModelTemplateResolver( $namespace, $config->getTemplatesRootPath(), $config->getTemplateFileExtension(), @@ -148,12 +148,12 @@ public function __construct( $modules->setEventDispatcher($eventDispatcher) ->setObjectReader($objectReader) ->setObjectPropertyWriter($objectPropertyWriter) - ->setModelTemplateProvider($templateProvider) + ->setModelTemplateResolver($templateProvider) ->setPropertyValueProvider($propertyValueProvider) ->setModelFactory($realModelFactory) ->setModelRenderer($realModelRenderer) - ->setModelNamespaceProvider($modelNamespaceProvider) - ->setModelNameProvider($modelNameProvider); + ->setModelNamespaceResolver($modelNamespaceProvider) + ->setModelNameResolver($modelNameProvider); $this->modules = $modules; } diff --git a/private-classes/View/ViewNamespaceModulesContainer.php b/private-classes/View/ViewNamespaceModulesContainer.php index 164efc8..bb2ecd7 100644 --- a/private-classes/View/ViewNamespaceModulesContainer.php +++ b/private-classes/View/ViewNamespaceModulesContainer.php @@ -19,7 +19,7 @@ public function __construct() $this->viewNamespaceModules = []; } - public function addNamespaceModules( + public function registerNamespaceModules( string $namespace, ViewNamespaceModules $viewNamespaceModules ): void { @@ -32,7 +32,7 @@ public function addNamespaceModules( }); } - public function getNamespaceModulesByModelNamespace(string $modelNamespace): ?ViewNamespaceModules + public function resolveNamespaceModules(string $modelNamespace): ?ViewNamespaceModules { $matchedNamespaces = array_filter($this->viewNamespaceModules, function ( ViewNamespaceModules $viewNamespaceModules, diff --git a/src/Interfaces/CodeExecutorInterface.php b/src/Interfaces/CodeRunnerInterface.php similarity index 55% rename from src/Interfaces/CodeExecutorInterface.php rename to src/Interfaces/CodeRunnerInterface.php index c52ff3c..2c5b31f 100644 --- a/src/Interfaces/CodeExecutorInterface.php +++ b/src/Interfaces/CodeRunnerInterface.php @@ -4,10 +4,10 @@ namespace Prosopo\Views\Interfaces; -interface CodeExecutorInterface +interface CodeRunnerInterface { /** * @param array $arguments */ - public function executeCode(string $code, array $arguments = []): void; + public function runCode(string $code, array $arguments = []): void; } diff --git a/src/Interfaces/EventDispatcherInterface.php b/src/Interfaces/EventDispatcherInterface.php index 7d5377b..5865cdb 100644 --- a/src/Interfaces/EventDispatcherInterface.php +++ b/src/Interfaces/EventDispatcherInterface.php @@ -24,10 +24,10 @@ public function removeEventListener(string $eventName, callable $eventListener): /** * @param array $eventDetails */ - public function attachEventDetails(string $eventName, array $eventDetails): void; + public function registerEventDetails(string $eventName, array $eventDetails): void; /** * @param array $eventDetails */ - public function detachEventDetails(string $eventName, array $eventDetails): void; + public function unregisterEventDetails(string $eventName, array $eventDetails): void; } diff --git a/src/Interfaces/Model/ModelFactoryInterface.php b/src/Interfaces/Model/ModelFactoryInterface.php index 268710f..10a196c 100644 --- a/src/Interfaces/Model/ModelFactoryInterface.php +++ b/src/Interfaces/Model/ModelFactoryInterface.php @@ -17,5 +17,5 @@ interface ModelFactoryInterface * * @throws Exception */ - public function makeModel(string $modelClass); + public function createModel(string $modelClass); } diff --git a/src/Interfaces/Model/ModelNameProviderInterface.php b/src/Interfaces/Model/ModelNameProviderInterface.php deleted file mode 100644 index 658934c..0000000 --- a/src/Interfaces/Model/ModelNameProviderInterface.php +++ /dev/null @@ -1,10 +0,0 @@ - $modelOrClass */ - public function getModelNamespace($modelOrClass): string; + public function resolveModelNamespace($modelOrClass): string; } diff --git a/src/Interfaces/Model/ModelRendererInterface.php b/src/Interfaces/Model/ModelRendererInterface.php index 961f9d5..ac3e2b3 100644 --- a/src/Interfaces/Model/ModelRendererInterface.php +++ b/src/Interfaces/Model/ModelRendererInterface.php @@ -13,9 +13,9 @@ interface ModelRendererInterface * @template T of TemplateModelInterface * * @param T|class-string $modelOrClass - * @param Closure(T):void|null $setupCallback + * @param Closure(T):void|null $setupModelCallback * * @throws Exception */ - public function renderModel($modelOrClass, Closure $setupCallback = null, bool $doPrint = false): string; + public function renderModel($modelOrClass, Closure $setupModelCallback = null): string; } diff --git a/src/Interfaces/Model/TemplateModelWithDefaultsInterface.php b/src/Interfaces/Model/TemplateModelWithDefaultsInterface.php index 3bd6f6e..ced9d9d 100644 --- a/src/Interfaces/Model/TemplateModelWithDefaultsInterface.php +++ b/src/Interfaces/Model/TemplateModelWithDefaultsInterface.php @@ -8,5 +8,5 @@ interface TemplateModelWithDefaultsInterface { - public function getDefaultsPropertyValueProvider(): PropertyValueProviderInterface; + public function getDefaultsProvider(): PropertyValueProviderInterface; } diff --git a/src/Interfaces/Object/ObjectPropertyWriterInterface.php b/src/Interfaces/Object/ObjectPropertyWriterInterface.php index ff5a1e8..948fd7e 100644 --- a/src/Interfaces/Object/ObjectPropertyWriterInterface.php +++ b/src/Interfaces/Object/ObjectPropertyWriterInterface.php @@ -6,8 +6,5 @@ interface ObjectPropertyWriterInterface { - public function setObjectPropertyValues( - object $instance, - PropertyValueProviderInterface $propertyValueProvider - ): void; + public function assignPropertyValues(object $instance, PropertyValueProviderInterface $propertyValueProvider): void; } diff --git a/src/Interfaces/Object/ObjectReaderInterface.php b/src/Interfaces/Object/ObjectReaderInterface.php index 0747ab2..a479e40 100644 --- a/src/Interfaces/Object/ObjectReaderInterface.php +++ b/src/Interfaces/Object/ObjectReaderInterface.php @@ -9,5 +9,5 @@ interface ObjectReaderInterface /** * @return array name => value (or callback) */ - public function getObjectVariables(object $instance): array; + public function extractObjectVariables(object $instance): array; } diff --git a/src/Interfaces/Template/ModelTemplateProviderInterface.php b/src/Interfaces/Template/ModelTemplateResolverInterface.php similarity index 53% rename from src/Interfaces/Template/ModelTemplateProviderInterface.php rename to src/Interfaces/Template/ModelTemplateResolverInterface.php index d3974f4..c6a0558 100644 --- a/src/Interfaces/Template/ModelTemplateProviderInterface.php +++ b/src/Interfaces/Template/ModelTemplateResolverInterface.php @@ -6,7 +6,7 @@ use Prosopo\Views\Interfaces\Model\TemplateModelInterface; -interface ModelTemplateProviderInterface +interface ModelTemplateResolverInterface { - public function getModelTemplate(TemplateModelInterface $model): string; + public function resolveModelTemplate(TemplateModelInterface $model): string; } diff --git a/src/Interfaces/Template/TemplateRendererInterface.php b/src/Interfaces/Template/TemplateRendererInterface.php index 9520467..29de7ca 100644 --- a/src/Interfaces/Template/TemplateRendererInterface.php +++ b/src/Interfaces/Template/TemplateRendererInterface.php @@ -9,5 +9,5 @@ interface TemplateRendererInterface /** * @param array $variables */ - public function renderTemplate(string $template, array $variables = [], bool $doPrint = false): string; + public function renderTemplate(string $template, array $variables = []): string; } diff --git a/src/Interfaces/View/ViewNamespaceManagerInterface.php b/src/Interfaces/View/ViewNamespaceManagerInterface.php index f0ea583..cb6c1ce 100644 --- a/src/Interfaces/View/ViewNamespaceManagerInterface.php +++ b/src/Interfaces/View/ViewNamespaceManagerInterface.php @@ -9,5 +9,5 @@ interface ViewNamespaceManagerInterface { - public function addNamespace(string $namespace, ViewNamespaceConfig $config): ViewNamespaceModules; + public function registerNamespace(string $namespace, ViewNamespaceConfig $config): ViewNamespaceModules; } diff --git a/src/Interfaces/View/ViewNamespaceModulesContainerInterface.php b/src/Interfaces/View/ViewNamespaceModulesContainerInterface.php index 3f207a6..8a07407 100644 --- a/src/Interfaces/View/ViewNamespaceModulesContainerInterface.php +++ b/src/Interfaces/View/ViewNamespaceModulesContainerInterface.php @@ -8,7 +8,7 @@ interface ViewNamespaceModulesContainerInterface { - public function addNamespaceModules(string $namespace, ViewNamespaceModules $viewNamespaceModules): void; + public function registerNamespaceModules(string $namespace, ViewNamespaceModules $viewNamespaceModules): void; - public function getNamespaceModulesByModelNamespace(string $modelNamespace): ?ViewNamespaceModules; + public function resolveNamespaceModules(string $modelNamespace): ?ViewNamespaceModules; } diff --git a/src/TemplateModel.php b/src/TemplateModel.php index 1dc2cdc..9825b2f 100644 --- a/src/TemplateModel.php +++ b/src/TemplateModel.php @@ -47,10 +47,10 @@ final public function __construct( public function getTemplateArguments(): array { - return $this->objectReader->getObjectVariables($this); + return $this->objectReader->extractObjectVariables($this); } - public function getDefaultsPropertyValueProvider(): PropertyValueProviderInterface + public function getDefaultsProvider(): PropertyValueProviderInterface { return $this->propertyValueProviderForDefaults; } diff --git a/src/View/ViewNamespaceModules.php b/src/View/ViewNamespaceModules.php index b4c6aa4..a161a35 100644 --- a/src/View/ViewNamespaceModules.php +++ b/src/View/ViewNamespaceModules.php @@ -6,13 +6,13 @@ use Prosopo\Views\Interfaces\EventDispatcherInterface; use Prosopo\Views\Interfaces\Model\ModelFactoryInterface; -use Prosopo\Views\Interfaces\Model\ModelNameProviderInterface; -use Prosopo\Views\Interfaces\Model\ModelNamespaceProviderInterface; +use Prosopo\Views\Interfaces\Model\ModelNameResolverInterface; +use Prosopo\Views\Interfaces\Model\ModelNamespaceResolverInterface; use Prosopo\Views\Interfaces\Model\ModelRendererInterface; use Prosopo\Views\Interfaces\Object\ObjectPropertyWriterInterface; use Prosopo\Views\Interfaces\Object\ObjectReaderInterface; use Prosopo\Views\Interfaces\Object\PropertyValueProviderInterface; -use Prosopo\Views\Interfaces\Template\ModelTemplateProviderInterface; +use Prosopo\Views\Interfaces\Template\ModelTemplateResolverInterface; use Prosopo\Views\Interfaces\Template\TemplateRendererInterface; /** @@ -28,27 +28,27 @@ final class ViewNamespaceModules //// Custom modules: define them only when you need to override the default behavior: private ?ModelFactoryInterface $modelFactory; - private ?ModelTemplateProviderInterface $modelTemplateProvider; + private ?ModelTemplateResolverInterface $modelTemplateResolver; private ?ObjectReaderInterface $objectReader; private ?ObjectPropertyWriterInterface $objectPropertyWriter; private ?PropertyValueProviderInterface $propertyValueProvider; private ?ModelRendererInterface $modelRenderer; private ?EventDispatcherInterface $eventDispatcher; - private ?ModelNameProviderInterface $modelNameProvider; - private ?ModelNamespaceProviderInterface $modelNamespaceProvider; + private ?ModelNameResolverInterface $modelNameResolver; + private ?ModelNamespaceResolverInterface $modelNamespaceResolver; public function __construct(TemplateRendererInterface $templateRenderer) { $this->templateRenderer = $templateRenderer; $this->modelFactory = null; - $this->modelTemplateProvider = null; + $this->modelTemplateResolver = null; $this->objectReader = null; $this->objectPropertyWriter = null; $this->propertyValueProvider = null; $this->modelRenderer = null; $this->eventDispatcher = null; - $this->modelNameProvider = null; - $this->modelNamespaceProvider = null; + $this->modelNameResolver = null; + $this->modelNamespaceResolver = null; } //// Getters. @@ -63,9 +63,9 @@ public function getModelFactory(): ?ModelFactoryInterface return $this->modelFactory; } - public function getModelTemplateProvider(): ?ModelTemplateProviderInterface + public function getModelTemplateResolver(): ?ModelTemplateResolverInterface { - return $this->modelTemplateProvider; + return $this->modelTemplateResolver; } public function getObjectReader(): ?ObjectReaderInterface @@ -93,14 +93,14 @@ public function getEventDispatcher(): ?EventDispatcherInterface return $this->eventDispatcher; } - public function getModelNameProvider(): ?ModelNameProviderInterface + public function getModelNameResolver(): ?ModelNameResolverInterface { - return $this->modelNameProvider; + return $this->modelNameResolver; } - public function getModelNamespaceProvider(): ?ModelNamespaceProviderInterface + public function getModelNamespaceResolver(): ?ModelNamespaceResolverInterface { - return $this->modelNamespaceProvider; + return $this->modelNamespaceResolver; } //// Setters. @@ -119,9 +119,9 @@ public function setModelFactory(?ModelFactoryInterface $viewFactory): self return $this; } - public function setModelTemplateProvider(?ModelTemplateProviderInterface $modelTemplateProvider): self + public function setModelTemplateResolver(?ModelTemplateResolverInterface $modelTemplateResolver): self { - $this->modelTemplateProvider = $modelTemplateProvider; + $this->modelTemplateResolver = $modelTemplateResolver; return $this; } @@ -161,16 +161,16 @@ public function setEventDispatcher(?EventDispatcherInterface $eventDispatcher): return $this; } - public function setModelNameProvider(?ModelNameProviderInterface $modelNameProvider): self + public function setModelNameResolver(?ModelNameResolverInterface $modelNameResolver): self { - $this->modelNameProvider = $modelNameProvider; + $this->modelNameResolver = $modelNameResolver; return $this; } - public function setModelNamespaceProvider(?ModelNamespaceProviderInterface $modelNamespaceProvider): self + public function setModelNamespaceResolver(?ModelNamespaceResolverInterface $modelNamespaceResolver): self { - $this->modelNamespaceProvider = $modelNamespaceProvider; + $this->modelNamespaceResolver = $modelNamespaceResolver; return $this; } diff --git a/src/View/ViewTemplateRenderer.php b/src/View/ViewTemplateRenderer.php index 23231a0..e08f432 100644 --- a/src/View/ViewTemplateRenderer.php +++ b/src/View/ViewTemplateRenderer.php @@ -6,10 +6,10 @@ use Prosopo\Views\Interfaces\Template\TemplateRendererInterface; use Prosopo\Views\PrivateClasses\Blade\BladeCompiler; -use Prosopo\Views\PrivateClasses\CodeExecutor\CodeExecutorWithErrorHandler; -use Prosopo\Views\PrivateClasses\CodeExecutor\CodeExecutorWithGlobalArguments; -use Prosopo\Views\PrivateClasses\CodeExecutor\CodeExecutorWithTemplateCompilation; -use Prosopo\Views\PrivateClasses\CodeExecutor\PhpCodeExecutor; +use Prosopo\Views\PrivateClasses\CodeRunner\CodeRunnerWithErrorHandler; +use Prosopo\Views\PrivateClasses\CodeRunner\CodeRunnerWithGlobalArguments; +use Prosopo\Views\PrivateClasses\CodeRunner\CodeRunnerWithTemplateCompilation; +use Prosopo\Views\PrivateClasses\CodeRunner\PhpCodeRunner; use Prosopo\Views\PrivateClasses\EventDispatcher; use Prosopo\Views\PrivateClasses\Template\TemplateRenderer; use Prosopo\Views\PrivateClasses\Template\TemplateRendererWithCustomEscape; @@ -53,12 +53,12 @@ public function __construct(?ViewTemplateRendererConfig $config = null) $codeExecutor = $modules->getCodeExecutor(); $codeExecutor = null === $codeExecutor ? - new PhpCodeExecutor() : + new PhpCodeRunner() : $codeExecutor; - $codeExecutor = new CodeExecutorWithErrorHandler($codeExecutor, $eventDispatcher, $errorEventName); - $codeExecutor = new CodeExecutorWithGlobalArguments($codeExecutor, $config->getGlobalVariables()); - $codeExecutor = new CodeExecutorWithTemplateCompilation($codeExecutor, $templateCompiler); + $codeExecutor = new CodeRunnerWithErrorHandler($codeExecutor, $eventDispatcher, $errorEventName); + $codeExecutor = new CodeRunnerWithGlobalArguments($codeExecutor, $config->getGlobalVariables()); + $codeExecutor = new CodeRunnerWithTemplateCompilation($codeExecutor, $templateCompiler); $templateRenderer = $modules->getTemplateRenderer(); $templateRenderer = null === $templateRenderer ? @@ -85,9 +85,9 @@ public function __construct(?ViewTemplateRendererConfig $config = null) $this->templateRenderer = $templateRenderer; } - public function renderTemplate(string $template, array $variables = [], bool $doPrint = false): string + public function renderTemplate(string $template, array $variables = []): string { - return $this->templateRenderer->renderTemplate($template, $variables, $doPrint); + return $this->templateRenderer->renderTemplate($template, $variables); } public function getModules(): ViewTemplateRendererModules diff --git a/src/View/ViewTemplateRendererModules.php b/src/View/ViewTemplateRendererModules.php index 7a82cb7..583555f 100644 --- a/src/View/ViewTemplateRendererModules.php +++ b/src/View/ViewTemplateRendererModules.php @@ -4,7 +4,7 @@ namespace Prosopo\Views\View; -use Prosopo\Views\Interfaces\CodeExecutorInterface; +use Prosopo\Views\Interfaces\CodeRunnerInterface; use Prosopo\Views\Interfaces\EventDispatcherInterface; use Prosopo\Views\Interfaces\Template\TemplateCompilerInterface; use Prosopo\Views\Interfaces\Template\TemplateRendererInterface; @@ -20,7 +20,7 @@ final class ViewTemplateRendererModules private ?TemplateRendererInterface $templateRenderer; private ?EventDispatcherInterface $eventDispatcher; private ?TemplateCompilerInterface $templateCompiler; - private ?CodeExecutorInterface $phpCodeExecutor; + private ?CodeRunnerInterface $phpCodeExecutor; public function __construct() { @@ -47,7 +47,7 @@ public function getTemplateCompiler(): ?TemplateCompilerInterface return $this->templateCompiler; } - public function getCodeExecutor(): ?CodeExecutorInterface + public function getCodeExecutor(): ?CodeRunnerInterface { return $this->phpCodeExecutor; } @@ -75,7 +75,7 @@ public function setTemplateCompiler(?TemplateCompilerInterface $templateCompiler return $this; } - public function setCodeExecutor(?CodeExecutorInterface $codeExecutor): self + public function setCodeExecutor(?CodeRunnerInterface $codeExecutor): self { $this->phpCodeExecutor = $codeExecutor; diff --git a/src/Views.php b/src/Views.php index bb8d698..01b9f4b 100644 --- a/src/Views.php +++ b/src/Views.php @@ -7,12 +7,12 @@ use Closure; use Exception; use Prosopo\Views\Interfaces\Model\ModelFactoryInterface; -use Prosopo\Views\Interfaces\Model\ModelNamespaceProviderInterface; +use Prosopo\Views\Interfaces\Model\ModelNamespaceResolverInterface; use Prosopo\Views\Interfaces\Model\ModelRendererInterface; use Prosopo\Views\Interfaces\Model\TemplateModelInterface; use Prosopo\Views\Interfaces\View\ViewNamespaceManagerInterface; use Prosopo\Views\Interfaces\View\ViewNamespaceModulesContainerInterface; -use Prosopo\Views\PrivateClasses\Model\ModelNamespaceProvider; +use Prosopo\Views\PrivateClasses\Model\ModelNamespaceResolver; use Prosopo\Views\PrivateClasses\Object\ObjectClassReader; use Prosopo\Views\PrivateClasses\View\ViewNamespace; use Prosopo\Views\PrivateClasses\View\ViewNamespaceModulesContainer; @@ -27,7 +27,7 @@ final class Views implements ViewNamespaceManagerInterface, ModelFactoryInterfac { private string $namespaceNotFoundErrorMessage; private string $wrongModelErrorMessage; - private ModelNamespaceProviderInterface $modelNamespaceProvider; + private ModelNamespaceResolverInterface $modelNamespaceProvider; private ViewNamespaceModulesContainerInterface $namespaceModulesContainer; public function __construct(?ViewsConfig $config = null) @@ -38,7 +38,7 @@ public function __construct(?ViewsConfig $config = null) $modelNamespaceProvider = $config->getModelNamespaceProvider(); $this->modelNamespaceProvider = null === $modelNamespaceProvider ? - new ModelNamespaceProvider(new ObjectClassReader()) : + new ModelNamespaceResolver(new ObjectClassReader()) : $modelNamespaceProvider; $namespaceModulesContainer = $config->getNamespaceModulesContainer(); @@ -50,25 +50,25 @@ public function __construct(?ViewsConfig $config = null) $this->wrongModelErrorMessage = $config->getWrongModelErrorMessage(); } - public function addNamespace(string $namespace, ViewNamespaceConfig $config): ViewNamespaceModules + public function registerNamespace(string $namespace, ViewNamespaceConfig $config): ViewNamespaceModules { $viewNamespace = $this->makeViewNamespace($namespace, $config); $viewNamespaceModules = $viewNamespace->getModules(); - $this->namespaceModulesContainer->addNamespaceModules($namespace, $viewNamespaceModules); + $this->namespaceModulesContainer->registerNamespaceModules($namespace, $viewNamespaceModules); return $viewNamespaceModules; } - public function makeModel(string $modelClass) + public function createModel(string $modelClass) { if (false === $this->isModel($modelClass)) { throw $this->makeWrongModelException($modelClass); } - $modelNamespace = $this->modelNamespaceProvider->getModelNamespace($modelClass); - $namespaceModules = $this->namespaceModulesContainer->getNamespaceModulesByModelNamespace($modelNamespace); + $modelNamespace = $this->modelNamespaceProvider->resolveModelNamespace($modelClass); + $namespaceModules = $this->namespaceModulesContainer->resolveNamespaceModules($modelNamespace); if (null === $namespaceModules) { throw $this->makeNamespaceNotResolvedException($modelNamespace); @@ -80,17 +80,17 @@ public function makeModel(string $modelClass) throw $this->makeNamespaceNotResolvedException($modelNamespace); } - return $modelFactory->makeModel($modelClass); + return $modelFactory->createModel($modelClass); } - public function renderModel($modelOrClass, Closure $setupCallback = null, bool $doPrint = false): string + public function renderModel($modelOrClass, Closure $setupModelCallback = null): string { if (false === $this->isModel($modelOrClass)) { throw $this->makeWrongModelException($modelOrClass); } - $modelNamespace = $this->modelNamespaceProvider->getModelNamespace($modelOrClass); - $namespaceModules = $this->namespaceModulesContainer->getNamespaceModulesByModelNamespace($modelNamespace); + $modelNamespace = $this->modelNamespaceProvider->resolveModelNamespace($modelOrClass); + $namespaceModules = $this->namespaceModulesContainer->resolveNamespaceModules($modelNamespace); if (null === $namespaceModules) { throw $this->makeNamespaceNotResolvedException($modelNamespace); @@ -102,7 +102,7 @@ public function renderModel($modelOrClass, Closure $setupCallback = null, bool $ throw $this->makeNamespaceNotResolvedException($modelNamespace); } - return $modelRenderer->renderModel($modelOrClass, $setupCallback, $doPrint); + return $modelRenderer->renderModel($modelOrClass, $setupModelCallback); } protected function makeViewNamespace(string $namespace, ViewNamespaceConfig $config): ViewNamespace diff --git a/src/ViewsConfig.php b/src/ViewsConfig.php index d49c557..25661bc 100644 --- a/src/ViewsConfig.php +++ b/src/ViewsConfig.php @@ -4,7 +4,7 @@ namespace Prosopo\Views; -use Prosopo\Views\Interfaces\Model\ModelNamespaceProviderInterface; +use Prosopo\Views\Interfaces\Model\ModelNamespaceResolverInterface; use Prosopo\Views\Interfaces\View\ViewNamespaceModulesContainerInterface; /** @@ -15,7 +15,7 @@ final class ViewsConfig { private string $namespaceNotFoundErrorMessage; private string $wrongModelErrorMessage; - private ?ModelNamespaceProviderInterface $modelNamespaceProvider; + private ?ModelNamespaceResolverInterface $modelNamespaceProvider; private ?ViewNamespaceModulesContainerInterface $namespaceModulesContainer; public function __construct() @@ -38,7 +38,7 @@ public function getWrongModelErrorMessage(): string return $this->wrongModelErrorMessage; } - public function getModelNamespaceProvider(): ?ModelNamespaceProviderInterface + public function getModelNamespaceProvider(): ?ModelNamespaceResolverInterface { return $this->modelNamespaceProvider; } @@ -64,7 +64,7 @@ public function setWrongModelErrorMessage(string $wrongModelErrorMessage): self return $this; } - public function setModelNamespaceProvider(?ModelNamespaceProviderInterface $modelNamespaceProvider): self + public function setModelNamespaceProvider(?ModelNamespaceResolverInterface $modelNamespaceProvider): self { $this->modelNamespaceProvider = $modelNamespaceProvider; diff --git a/tests/pest/Feature/ViewsTest.php b/tests/pest/Feature/ViewsTest.php index 4742d10..35f64be 100644 --- a/tests/pest/Feature/ViewsTest.php +++ b/tests/pest/Feature/ViewsTest.php @@ -43,7 +43,7 @@ public function testRenderModelThatImplementsInterface(): void $views = new Views(); // when - $views->addNamespace($modelNamespace, $namespaceConfig); + $views->registerNamespace($modelNamespace, $namespaceConfig); $modelClass = $modelNamespace . '\\FirstModel'; $model = new $modelClass(); @@ -83,7 +83,7 @@ public function testRenderModelThatImplementInterfaceIgnoresPublicProperties(): $views = new Views(); // when - $views->addNamespace($modelNamespace, $namespaceConfig); + $views->registerNamespace($modelNamespace, $namespaceConfig); $modelClass = $modelNamespace . '\\FirstModel'; $model = new $modelClass(); @@ -116,10 +116,10 @@ public function testRenderModelThatExtendsBaseClass(): void ); // when - $views->addNamespace($modelNamespace, $namespaceConfig); + $views->registerNamespace($modelNamespace, $namespaceConfig); $modelClass = $modelNamespace . '\\FirstModel'; - $model = $views->makeModel($modelClass); + $model = $views->createModel($modelClass); $model->message = 'Hello World!'; // then @@ -150,7 +150,7 @@ public function testRenderCallsSetupCallback(): void ); // when - $views->addNamespace($modelNamespace, $namespaceConfig); + $views->registerNamespace($modelNamespace, $namespaceConfig); $modelClass = $modelNamespace . '\\FirstModel'; @@ -187,7 +187,7 @@ public function testRenderModelPassesStringToTemplateRendererWhenFileBasedModeIs $views = new Views(); // when - $views->addNamespace($modelNamespace, $namespaceConfig); + $views->registerNamespace($modelNamespace, $namespaceConfig); $modelClass = $modelNamespace . '\\FirstModel'; $model = new $modelClass(); @@ -227,7 +227,7 @@ public function testRenderCallsTemplateErrorHandler(): void ); // when - $views->addNamespace($modelNamespace, $namespaceConfig); + $views->registerNamespace($modelNamespace, $namespaceConfig); $modelClass = $modelNamespace . '\\FirstModel'; $rendered = $views->renderModel($modelClass, (function ($model) { $model->message = 'some data'; @@ -274,7 +274,7 @@ public function testRenderNotCallTemplateErrorHandlerWithoutReason(): void ); // when - $views->addNamespace($modelNamespace, $namespaceConfig); + $views->registerNamespace($modelNamespace, $namespaceConfig); $modelClass = $modelNamespace . '\\FirstModel'; $rendered = $views->renderModel($modelClass, (function ($model) { $model->message = 'some data'; @@ -314,7 +314,7 @@ public function testRenderSupportsInnerNamespaces(): void $views = new Views(); // when - $views->addNamespace($namespace, $namespaceConfig); + $views->registerNamespace($namespace, $namespaceConfig); $modelClass = $innerModelNamespace . '\\FirstModel'; $model = new $modelClass(); @@ -366,8 +366,8 @@ public function testRenderSupportsDifferentNamespaces(): void $views = new Views(); // when - $views->addNamespace($firstNamespace, $firstNamespaceConfig); - $views->addNamespace($secondNamespace, $secondNamespaceConfig); + $views->registerNamespace($firstNamespace, $firstNamespaceConfig); + $views->registerNamespace($secondNamespace, $secondNamespaceConfig); $firstModelClass = $firstNamespace . '\\FirstModel'; $firstModel = new $firstModelClass(); @@ -409,7 +409,7 @@ public function testRenderIncludesInnerModel(): void $views = new Views(); // when - $views->addNamespace($modelNamespace, $namespaceConfig); + $views->registerNamespace($modelNamespace, $namespaceConfig); $innerModelClass = $modelNamespace . '\\InnerModel'; $topModelClass = $modelNamespace . '\\TopModel'; @@ -455,7 +455,7 @@ public function compileTemplate(string $template): string ->setTemplatesRootPath(vfsStream::url('templates')) ->setTemplateFileExtension('.php'); - $views->addNamespace($modelNamespace, $viewNamespaceConfig); + $views->registerNamespace($modelNamespace, $viewNamespaceConfig); $modelClass = $modelNamespace . '\\Pure'; $model = new $modelClass(); @@ -499,8 +499,8 @@ public function testRenderIncludesInnerModelFromDifferentNamespace(): void $views = new Views(); // when - $views->addNamespace($firstNamespace, $firstNamespaceConfig); - $views->addNamespace($secondNamespace, $secondNamespaceConfig); + $views->registerNamespace($firstNamespace, $firstNamespaceConfig); + $views->registerNamespace($secondNamespace, $secondNamespaceConfig); $innerModelClass = $secondNamespace . '\\InnerModel'; $topModelClass = $firstNamespace . '\\TopModel'; @@ -528,10 +528,10 @@ public function testMakeModelThatExtendsBaseClass(): void ); // when - $views->addNamespace($modelNamespace, $namespaceConfig); + $views->registerNamespace($modelNamespace, $namespaceConfig); $modelClass = $modelNamespace . '\\FirstModel'; - $model = $views->makeModel($modelClass); + $model = $views->createModel($modelClass); // then $this->assertSame($modelClass, get_class($model)); @@ -558,10 +558,10 @@ public function testMakeModelImplementsInterface(): void ); // when - $views->addNamespace($modelNamespace, $namespaceConfig); + $views->registerNamespace($modelNamespace, $namespaceConfig); $modelClass = $modelNamespace . '\\FirstModel'; - $model = $views->makeModel($modelClass); + $model = $views->createModel($modelClass); // then $this->assertSame($modelClass, get_class($model)); @@ -588,10 +588,10 @@ public function testMakeModelSetsDefaultsForModelsThatExtendBaseClass(): void ); // when - $views->addNamespace($modelNamespace, $namespaceConfig); + $views->registerNamespace($modelNamespace, $namespaceConfig); $modelClass = $modelNamespace . '\\FirstModel'; - $model = $views->makeModel($modelClass); + $model = $views->createModel($modelClass); // then $this->assertSame('', $model->message); @@ -618,10 +618,10 @@ public function testMakeModelNotSetDefaultsForModelsWithoutDefaultsInterface(): ); // when - $views->addNamespace($modelNamespace, $namespaceConfig); + $views->registerNamespace($modelNamespace, $namespaceConfig); $modelClass = $modelNamespace . '\\FirstModel'; - $model = $views->makeModel($modelClass); + $model = $views->createModel($modelClass); // then $this->assertFalse(isset($model->message)); @@ -648,15 +648,15 @@ public function testMakeModelSupportsDifferentNamespaces(): void $views = new Views(); // when - $views->addNamespace($firstNamespace, $firstNamespaceConfig); - $views->addNamespace($secondNamespace, $secondNamespaceConfig); + $views->registerNamespace($firstNamespace, $firstNamespaceConfig); + $views->registerNamespace($secondNamespace, $secondNamespaceConfig); $firstModelClass = $firstNamespace . '\\FirstModel'; $secondModelClass = $secondNamespace . '\\SecondModel'; // then - $this->assertSame($firstModelClass, get_class($views->makeModel($firstModelClass))); - $this->assertSame($secondModelClass, get_class($views->makeModel($secondModelClass))); + $this->assertSame($firstModelClass, get_class($views->createModel($firstModelClass))); + $this->assertSame($secondModelClass, get_class($views->createModel($secondModelClass))); } /** diff --git a/tests/pest/Unit/CodeExecutor/CodeExecutorWithErrorHandlerTest.php b/tests/pest/Unit/CodeRunner/CodeRunnerWithErrorHandlerTest.php similarity index 76% rename from tests/pest/Unit/CodeExecutor/CodeExecutorWithErrorHandlerTest.php rename to tests/pest/Unit/CodeRunner/CodeRunnerWithErrorHandlerTest.php index e8b9c56..bb682a6 100644 --- a/tests/pest/Unit/CodeExecutor/CodeExecutorWithErrorHandlerTest.php +++ b/tests/pest/Unit/CodeRunner/CodeRunnerWithErrorHandlerTest.php @@ -7,30 +7,30 @@ use Error; use Exception; use Mockery; -use Prosopo\Views\Interfaces\CodeExecutorInterface; +use Prosopo\Views\Interfaces\CodeRunnerInterface; use Prosopo\Views\Interfaces\EventDispatcherInterface; -use Prosopo\Views\PrivateClasses\CodeExecutor\CodeExecutorWithErrorHandler; +use Prosopo\Views\PrivateClasses\CodeRunner\CodeRunnerWithErrorHandler; use Tests\TestCase; use Throwable; -class CodeExecutorWithErrorHandlerTest extends TestCase +class CodeRunnerWithErrorHandlerTest extends TestCase { public function testNotCallsDispatcherWithoutReason(): void { // given - $codeExecutor = Mockery::mock(CodeExecutorInterface::class); + $codeExecutor = Mockery::mock(CodeRunnerInterface::class); $eventDispatcher = Mockery::mock(EventDispatcherInterface::class); - $contestant = new CodeExecutorWithErrorHandler( + $contestant = new CodeRunnerWithErrorHandler( $codeExecutor, $eventDispatcher, 'error' ); // when - $executeCode = fn() => $contestant->executeCode('correct code', ['var' => 1]); + $executeCode = fn() => $contestant->runCode('correct code', ['var' => 1]); // then - $codeExecutor->shouldReceive('executeCode') + $codeExecutor->shouldReceive('runCode') ->once(); $eventDispatcher->shouldNotReceive('dispatchEvent'); @@ -58,19 +58,19 @@ public function testCallsDispatcherOnWarning(): void protected function testCallsDispatcher(?Throwable $error, bool $isWarning = false): void { // given - $codeExecutor = Mockery::mock(CodeExecutorInterface::class); + $codeExecutor = Mockery::mock(CodeRunnerInterface::class); $eventDispatcher = Mockery::mock(EventDispatcherInterface::class); - $contestant = new CodeExecutorWithErrorHandler( + $contestant = new CodeRunnerWithErrorHandler( $codeExecutor, $eventDispatcher, 'errorEventName' ); // when - $executeCode = fn() => $contestant->executeCode('wrong code', ['var' => 1]); + $executeCode = fn() => $contestant->runCode('wrong code', ['var' => 1]); // then - $executorRule = $codeExecutor->shouldReceive('executeCode') + $executorRule = $codeExecutor->shouldReceive('runCode') ->once(); if (null !== $error) { diff --git a/tests/pest/Unit/CodeExecutor/CodeExecutorWithGlobalArgumentsTest.php b/tests/pest/Unit/CodeRunner/CodeRunnerWithGlobalArgumentsTest.php similarity index 56% rename from tests/pest/Unit/CodeExecutor/CodeExecutorWithGlobalArgumentsTest.php rename to tests/pest/Unit/CodeRunner/CodeRunnerWithGlobalArgumentsTest.php index b20a7c3..b1ac575 100644 --- a/tests/pest/Unit/CodeExecutor/CodeExecutorWithGlobalArgumentsTest.php +++ b/tests/pest/Unit/CodeRunner/CodeRunnerWithGlobalArgumentsTest.php @@ -6,22 +6,22 @@ use Mockery; use PHPUnit\Framework\TestCase; -use Prosopo\Views\Interfaces\CodeExecutorInterface; -use Prosopo\Views\PrivateClasses\CodeExecutor\CodeExecutorWithGlobalArguments; +use Prosopo\Views\Interfaces\CodeRunnerInterface; +use Prosopo\Views\PrivateClasses\CodeRunner\CodeRunnerWithGlobalArguments; -class CodeExecutorWithGlobalArgumentsTest extends TestCase +class CodeRunnerWithGlobalArgumentsTest extends TestCase { public function testMergesGlobalAndProvidedArgumentsBeforeExecutingCode(): void { // given - $codeExecutor = Mockery::mock(CodeExecutorInterface::class); - $executor = new CodeExecutorWithGlobalArguments($codeExecutor, ['globalKey' => 'globalValue']); + $codeExecutor = Mockery::mock(CodeRunnerInterface::class); + $executor = new CodeRunnerWithGlobalArguments($codeExecutor, ['globalKey' => 'globalValue']); // when - $executeCode = fn() => $executor->executeCode('sample code', ['key' => 'value']); + $executeCode = fn() => $executor->runCode('sample code', ['key' => 'value']); // then - $codeExecutor->shouldReceive('executeCode') + $codeExecutor->shouldReceive('runCode') ->once() ->with('sample code', [ 'globalKey' => 'globalValue', @@ -37,14 +37,14 @@ public function testMergesGlobalAndProvidedArgumentsBeforeExecutingCode(): void public function testUsesOnlyGlobalArgumentsWhenNoAdditionalArgumentsAreProvided(): void { // given - $codeExecutor = Mockery::mock(CodeExecutorInterface::class); - $executor = new CodeExecutorWithGlobalArguments($codeExecutor, ['globalKey' => 'globalValue']); + $codeExecutor = Mockery::mock(CodeRunnerInterface::class); + $executor = new CodeRunnerWithGlobalArguments($codeExecutor, ['globalKey' => 'globalValue']); // when - $executeCode = fn() => $executor->executeCode('sample code', []); + $executeCode = fn() => $executor->runCode('sample code', []); // then - $codeExecutor->shouldReceive('executeCode') + $codeExecutor->shouldReceive('runCode') ->once() ->with('sample code', [ 'globalKey' => 'globalValue', @@ -59,14 +59,14 @@ public function testUsesOnlyGlobalArgumentsWhenNoAdditionalArgumentsAreProvided( public function testOverridesGlobalArgumentsWithProvidedArgumentsIfKeysOverlap(): void { // given - $codeExecutor = Mockery::mock(CodeExecutorInterface::class); - $executor = new CodeExecutorWithGlobalArguments($codeExecutor, ['key' => 'globalValue']); + $codeExecutor = Mockery::mock(CodeRunnerInterface::class); + $executor = new CodeRunnerWithGlobalArguments($codeExecutor, ['key' => 'globalValue']); // when - $executeCode = fn() => $executor->executeCode('sample code', ['key' => 'providedValue']); + $executeCode = fn() => $executor->runCode('sample code', ['key' => 'providedValue']); // then - $codeExecutor->shouldReceive('executeCode') + $codeExecutor->shouldReceive('runCode') ->once() ->with('sample code', [ 'key' => 'providedValue', diff --git a/tests/pest/Unit/CodeExecutor/CodeExecutorWithTemplateCompilerTest.php b/tests/pest/Unit/CodeRunner/CodeRunnerWithTemplateCompilerTest.php similarity index 63% rename from tests/pest/Unit/CodeExecutor/CodeExecutorWithTemplateCompilerTest.php rename to tests/pest/Unit/CodeRunner/CodeRunnerWithTemplateCompilerTest.php index 824af64..38b9c52 100644 --- a/tests/pest/Unit/CodeExecutor/CodeExecutorWithTemplateCompilerTest.php +++ b/tests/pest/Unit/CodeRunner/CodeRunnerWithTemplateCompilerTest.php @@ -6,21 +6,21 @@ use Mockery; use PHPUnit\Framework\TestCase; -use Prosopo\Views\Interfaces\CodeExecutorInterface; +use Prosopo\Views\Interfaces\CodeRunnerInterface; use Prosopo\Views\Interfaces\Template\TemplateCompilerInterface; -use Prosopo\Views\PrivateClasses\CodeExecutor\CodeExecutorWithTemplateCompilation; +use Prosopo\Views\PrivateClasses\CodeRunner\CodeRunnerWithTemplateCompilation; -class CodeExecutorWithTemplateCompilationTest extends TestCase +class CodeRunnerWithTemplateCompilationTest extends TestCase { public function testExecutesCompiledTemplateCode(): void { // given - $codeExecutor = Mockery::mock(CodeExecutorInterface::class); + $codeExecutor = Mockery::mock(CodeRunnerInterface::class); $templateCompiler = Mockery::mock(TemplateCompilerInterface::class); - $contestant = new CodeExecutorWithTemplateCompilation($codeExecutor, $templateCompiler); + $contestant = new CodeRunnerWithTemplateCompilation($codeExecutor, $templateCompiler); // when - $executeCode = fn() => $contestant->executeCode('template code', ['arg1' => 'value1']); + $executeCode = fn() => $contestant->runCode('template code', ['arg1' => 'value1']); // then $templateCompiler->shouldReceive('compileTemplate') @@ -28,7 +28,7 @@ public function testExecutesCompiledTemplateCode(): void ->with('template code') ->andReturn('compiled code'); - $codeExecutor->shouldReceive('executeCode') + $codeExecutor->shouldReceive('runCode') ->once() ->with('compiled code', ['arg1' => 'value1']); @@ -41,12 +41,12 @@ public function testExecutesCompiledTemplateCode(): void public function testHandlesEmptyTemplateCode(): void { // given - $codeExecutor = Mockery::mock(CodeExecutorInterface::class); + $codeExecutor = Mockery::mock(CodeRunnerInterface::class); $templateCompiler = Mockery::mock(TemplateCompilerInterface::class); - $contestant = new CodeExecutorWithTemplateCompilation($codeExecutor, $templateCompiler); + $contestant = new CodeRunnerWithTemplateCompilation($codeExecutor, $templateCompiler); // when - $executeCode = fn() => $contestant->executeCode('', []); + $executeCode = fn() => $contestant->runCode('', []); // then $templateCompiler->shouldReceive('compileTemplate') @@ -54,7 +54,7 @@ public function testHandlesEmptyTemplateCode(): void ->with('') ->andReturn(''); - $codeExecutor->shouldReceive('executeCode') + $codeExecutor->shouldReceive('runCode') ->once() ->with('', []); @@ -67,12 +67,12 @@ public function testHandlesEmptyTemplateCode(): void public function testHandlesArgumentsCorrectly(): void { // given - $codeExecutor = Mockery::mock(CodeExecutorInterface::class); + $codeExecutor = Mockery::mock(CodeRunnerInterface::class); $templateCompiler = Mockery::mock(TemplateCompilerInterface::class); - $contestant = new CodeExecutorWithTemplateCompilation($codeExecutor, $templateCompiler); + $contestant = new CodeRunnerWithTemplateCompilation($codeExecutor, $templateCompiler); // when - $executeCode = fn() => $contestant->executeCode('template code with args', ['key1' => 'value1', 'key2' => 'value2']); + $executeCode = fn() => $contestant->runCode('template code with args', ['key1' => 'value1', 'key2' => 'value2']); // then $templateCompiler->shouldReceive('compileTemplate') @@ -80,7 +80,7 @@ public function testHandlesArgumentsCorrectly(): void ->with('template code with args') ->andReturn('compiled code with args'); - $codeExecutor->shouldReceive('executeCode') + $codeExecutor->shouldReceive('runCode') ->once() ->with('compiled code with args', ['key1' => 'value1', 'key2' => 'value2']); diff --git a/tests/pest/Unit/CodeExecutor/PhpCodeExecutorTest.php b/tests/pest/Unit/CodeRunner/PhpCodeRunnerTest.php similarity index 72% rename from tests/pest/Unit/CodeExecutor/PhpCodeExecutorTest.php rename to tests/pest/Unit/CodeRunner/PhpCodeRunnerTest.php index 1b8e476..c04c589 100644 --- a/tests/pest/Unit/CodeExecutor/PhpCodeExecutorTest.php +++ b/tests/pest/Unit/CodeRunner/PhpCodeRunnerTest.php @@ -5,19 +5,19 @@ namespace Tests\Unit\CodeExecutor; use PHPUnit\Framework\TestCase; -use Prosopo\Views\PrivateClasses\CodeExecutor\PhpCodeExecutor; +use Prosopo\Views\PrivateClasses\CodeRunner\PhpCodeRunner; -class PhpCodeExecutorTest extends TestCase +class PhpCodeRunnerTest extends TestCase { public function testExecutesCodeWithoutArguments(): void { // given - $executor = new PhpCodeExecutor(); + $executor = new PhpCodeRunner(); $code = 'executeCode($code, []); + $executor->runCode($code, []); // then $this->assertSame("Hello, World!", $output); @@ -26,13 +26,13 @@ public function testExecutesCodeWithoutArguments(): void public function testExecutesCodeWithArguments(): void { // given - $executor = new PhpCodeExecutor(); + $executor = new PhpCodeRunner(); $code = ' 10, 'arg2' => 20]; // when global $result; - $executor->executeCode($code, $arguments); + $executor->runCode($code, $arguments); // then $this->assertSame(30, $result); @@ -41,12 +41,12 @@ public function testExecutesCodeWithArguments(): void public function testExecutesCodeWithEchoStatement(): void { // given - $executor = new PhpCodeExecutor(); + $executor = new PhpCodeRunner(); $code = 'executeCode($code, []); + $executor->runCode($code, []); $response = ob_get_clean(); // apply diff --git a/tests/pest/Unit/EventDispatcherTest.php b/tests/pest/Unit/EventDispatcherTest.php index e892a9b..37bf2e8 100644 --- a/tests/pest/Unit/EventDispatcherTest.php +++ b/tests/pest/Unit/EventDispatcherTest.php @@ -92,7 +92,7 @@ public function testAttachEventDetails(): void }; // when - $dispatcher->attachEventDetails('testEvent', ['attachedKey' => 'attachedValue']); + $dispatcher->registerEventDetails('testEvent', ['attachedKey' => 'attachedValue']); $dispatcher->addEventListener('testEvent', $listener); $dispatcher->dispatchEvent('testEvent', ['key' => 'value']); @@ -117,8 +117,8 @@ public function testDetachEventDetails(): void }; // when - $dispatcher->attachEventDetails('testEvent', ['key1' => 'value1', 'key2' => 'value2']); - $dispatcher->detachEventDetails('testEvent', ['key2' => 'value2']); + $dispatcher->registerEventDetails('testEvent', ['key1' => 'value1', 'key2' => 'value2']); + $dispatcher->unregisterEventDetails('testEvent', ['key2' => 'value2']); $dispatcher->addEventListener('testEvent', $listener); $dispatcher->dispatchEvent('testEvent', ['key3' => 'value3']); @@ -174,7 +174,7 @@ public function testEventDetailsAreIsolatedPerEvent(): void }; // when - $dispatcher->attachEventDetails('eventA', ['attachedKey' => 'attachedValue']); + $dispatcher->registerEventDetails('eventA', ['attachedKey' => 'attachedValue']); $dispatcher->addEventListener('eventB', $listener); $dispatcher->dispatchEvent('eventB', ['key' => 'value']); diff --git a/tests/pest/Unit/Model/ModelFactoryTest.php b/tests/pest/Unit/Model/ModelFactoryTest.php index 054e08d..0ad5871 100644 --- a/tests/pest/Unit/Model/ModelFactoryTest.php +++ b/tests/pest/Unit/Model/ModelFactoryTest.php @@ -40,7 +40,7 @@ public function getTemplateArguments(): array }; // when - $result = $factory->makeModel(get_class($modelClass)); + $result = $factory->createModel(get_class($modelClass)); // then $this->assertInstanceOf(get_class($modelClass), $result); @@ -59,7 +59,7 @@ public function testMakeModelThrowsExceptionForInvalidClass(): void $factory = new ModelFactory($objectReaderMock, $propertyValueProviderMock); // when - $makeModel = fn() => $factory->makeModel('NonExistentClass'); + $makeModel = fn() => $factory->createModel('NonExistentClass'); // when & then $this->expectException(Error::class); @@ -82,7 +82,7 @@ public function __construct(int $test) }; // when - $makeModel = fn() => $factory->makeModel(get_class($invalidModelClass)); + $makeModel = fn() => $factory->createModel(get_class($invalidModelClass)); // then $this->expectException(Error::class); diff --git a/tests/pest/Unit/Model/ModelFactoryWithDefaultsManagementTest.php b/tests/pest/Unit/Model/ModelFactoryWithDefaultsManagementTest.php index 8656724..15b081d 100644 --- a/tests/pest/Unit/Model/ModelFactoryWithDefaultsManagementTest.php +++ b/tests/pest/Unit/Model/ModelFactoryWithDefaultsManagementTest.php @@ -31,25 +31,25 @@ public function testMakeModelInitializesPropertiesWhenObjectImplementsWithDefaul ); // when - $result = fn() =>$factory->makeModel('ModelClass'); + $result = fn() =>$factory->createModel('ModelClass'); // then - $modelFactoryMock->shouldReceive('makeModel') + $modelFactoryMock->shouldReceive('createModel') ->once() ->with('ModelClass') ->andReturn($modelWithDefaultsMock); $modelWithDefaultsMock - ->shouldReceive('getDefaultsPropertyValueProvider') + ->shouldReceive('getDefaultsProvider') ->once() ->andReturn($propertyValueProviderMock); - $objectReaderMock->shouldReceive('getObjectVariables') + $objectReaderMock->shouldReceive('extractObjectVariables') ->once() ->with($modelWithDefaultsMock) ->andReturn([]); - $objectPropertyWriterMock->shouldReceive('setObjectPropertyValues') + $objectPropertyWriterMock->shouldReceive('assignPropertyValues') ->once() ->with($modelWithDefaultsMock, $propertyValueProviderMock); @@ -73,10 +73,10 @@ public function testMakeModelSkipsPropertiesInitializationWhenObjectWithoutDefau ); // when - $result = fn() =>$factory->makeModel('ModelClass'); + $result = fn() =>$factory->createModel('ModelClass'); // then - $modelFactoryMock->shouldReceive('makeModel') + $modelFactoryMock->shouldReceive('createModel') ->once() ->with('ModelClass') ->andReturn($modelMock); @@ -104,53 +104,53 @@ public function testMakeModelHandlesInnerObjectsWithDefaultsInterface(): void ); // when - $result = fn()=>$factory->makeModel('ModelClass'); + $result = fn()=>$factory->createModel('ModelClass'); // then - $modelFactoryMock->shouldReceive('makeModel') + $modelFactoryMock->shouldReceive('createModel') ->once() ->with('ModelClass') ->andReturn($mainModelMock); $mainModelMock - ->shouldReceive('getDefaultsPropertyValueProvider') + ->shouldReceive('getDefaultsProvider') ->once() ->andReturn($propertyValueProviderMock); - $objectReaderMock->shouldReceive('getObjectVariables') + $objectReaderMock->shouldReceive('extractObjectVariables') ->once() ->with($mainModelMock) ->andReturn(['inner1' => $innerModelMock1, 'inner2' => $innerModelMock2]); - $objectPropertyWriterMock->shouldReceive('setObjectPropertyValues') + $objectPropertyWriterMock->shouldReceive('assignPropertyValues') ->once() ->with($mainModelMock, $propertyValueProviderMock); $innerModelMock1 - ->shouldReceive('getDefaultsPropertyValueProvider') + ->shouldReceive('getDefaultsProvider') ->once() ->andReturn($propertyValueProviderMock); - $objectReaderMock->shouldReceive('getObjectVariables') + $objectReaderMock->shouldReceive('extractObjectVariables') ->once() ->with($innerModelMock1) ->andReturn([]); - $objectPropertyWriterMock->shouldReceive('setObjectPropertyValues') + $objectPropertyWriterMock->shouldReceive('assignPropertyValues') ->once() ->with($innerModelMock1, $propertyValueProviderMock); $innerModelMock2 - ->shouldReceive('getDefaultsPropertyValueProvider') + ->shouldReceive('getDefaultsProvider') ->once() ->andReturn($propertyValueProviderMock); - $objectReaderMock->shouldReceive('getObjectVariables') + $objectReaderMock->shouldReceive('extractObjectVariables') ->once() ->with($innerModelMock2) ->andReturn([]); - $objectPropertyWriterMock->shouldReceive('setObjectPropertyValues') + $objectPropertyWriterMock->shouldReceive('assignPropertyValues') ->once() ->with($innerModelMock2, $propertyValueProviderMock); @@ -176,25 +176,25 @@ public function testMakeModelSkipsInnerObjectsWithoutDefaultsInterface(): void ); // when - $result = fn()=>$factory->makeModel('ModelClass'); + $result = fn()=>$factory->createModel('ModelClass'); // then - $modelFactoryMock->shouldReceive('makeModel') + $modelFactoryMock->shouldReceive('createModel') ->once() ->with('ModelClass') ->andReturn($mainModelMock); $mainModelMock - ->shouldReceive('getDefaultsPropertyValueProvider') + ->shouldReceive('getDefaultsProvider') ->once() ->andReturn($propertyValueProviderMock); - $objectReaderMock->shouldReceive('getObjectVariables') + $objectReaderMock->shouldReceive('extractObjectVariables') ->once() ->with($mainModelMock) ->andReturn(['inner1' => $innerModelMock1,]); - $objectPropertyWriterMock->shouldReceive('setObjectPropertyValues') + $objectPropertyWriterMock->shouldReceive('assignPropertyValues') ->once() ->with($mainModelMock, $propertyValueProviderMock); diff --git a/tests/pest/Unit/Model/ModelNameProviderTest.php b/tests/pest/Unit/Model/ModelNameProviderTest.php index 0250b44..49377f9 100644 --- a/tests/pest/Unit/Model/ModelNameProviderTest.php +++ b/tests/pest/Unit/Model/ModelNameProviderTest.php @@ -7,7 +7,7 @@ use Mockery; use PHPUnit\Framework\TestCase; use Prosopo\Views\Interfaces\Model\TemplateModelInterface; -use Prosopo\Views\PrivateClasses\Model\ModelNameProvider; +use Prosopo\Views\PrivateClasses\Model\ModelNameResolver; use Prosopo\Views\PrivateClasses\Object\ObjectClassReader; class ModelNameProviderTest extends TestCase @@ -16,11 +16,11 @@ public function testGetModelNameReturnsClassName(): void { // given $objectClassReaderMock = Mockery::mock(ObjectClassReader::class); - $provider = new ModelNameProvider($objectClassReaderMock); + $provider = new ModelNameResolver($objectClassReaderMock); $modelMock = Mockery::mock(TemplateModelInterface::class); // when - $getModelName = fn()=> $provider->getModelName($modelMock); + $getModelName = fn()=> $provider->resolveModelName($modelMock); // then $objectClassReaderMock @@ -37,11 +37,11 @@ public function testGetModelNameReturnsClassNameForRoot(): void { // given $objectClassReaderMock = Mockery::mock(ObjectClassReader::class); - $provider = new ModelNameProvider($objectClassReaderMock); + $provider = new ModelNameResolver($objectClassReaderMock); $modelMock = Mockery::mock(TemplateModelInterface::class); // when - $getModelNamespace = fn()=> $provider->getModelName($modelMock); + $getModelNamespace = fn()=> $provider->resolveModelName($modelMock); // then $objectClassReaderMock @@ -58,11 +58,11 @@ public function testGetModelNameReturnsClassNameForDeeplyNested(): void { // given $objectClassReaderMock = Mockery::mock(ObjectClassReader::class); - $provider = new ModelNameProvider($objectClassReaderMock); + $provider = new ModelNameResolver($objectClassReaderMock); $modelMock = Mockery::mock(TemplateModelInterface::class); // when - $getModelNamespace = fn()=> $provider->getModelName($modelMock); + $getModelNamespace = fn()=> $provider->resolveModelName($modelMock); // then $objectClassReaderMock diff --git a/tests/pest/Unit/Model/ModelNamespaceProviderTest.php b/tests/pest/Unit/Model/ModelNamespaceProviderTest.php index 90dcd70..06bdebb 100644 --- a/tests/pest/Unit/Model/ModelNamespaceProviderTest.php +++ b/tests/pest/Unit/Model/ModelNamespaceProviderTest.php @@ -7,7 +7,7 @@ use Mockery; use PHPUnit\Framework\TestCase; use Prosopo\Views\Interfaces\Model\TemplateModelInterface; -use Prosopo\Views\PrivateClasses\Model\ModelNamespaceProvider; +use Prosopo\Views\PrivateClasses\Model\ModelNamespaceResolver; use Prosopo\Views\PrivateClasses\Object\ObjectClassReader; class ModelNamespaceProviderTest extends TestCase @@ -16,11 +16,11 @@ public function testGetModelNamespaceReturnsNamespace(): void { // given $objectClassReaderMock = Mockery::mock(ObjectClassReader::class); - $provider = new ModelNamespaceProvider($objectClassReaderMock); + $provider = new ModelNamespaceResolver($objectClassReaderMock); $modelMock = Mockery::mock(TemplateModelInterface::class); // when - $getModelNamespace = fn()=> $provider->getModelNamespace($modelMock); + $getModelNamespace = fn()=> $provider->resolveModelNamespace($modelMock); // then $objectClassReaderMock @@ -37,11 +37,11 @@ public function testGetModelNamespaceReturnsNamespaceForRoot(): void { // given $objectClassReaderMock = Mockery::mock(ObjectClassReader::class); - $provider = new ModelNamespaceProvider($objectClassReaderMock); + $provider = new ModelNamespaceResolver($objectClassReaderMock); $modelMock = Mockery::mock(TemplateModelInterface::class); // when - $getModelNamespace = fn()=> $provider->getModelNamespace($modelMock); + $getModelNamespace = fn()=> $provider->resolveModelNamespace($modelMock); // then $objectClassReaderMock @@ -58,11 +58,11 @@ public function testGetModelNamespaceReturnsNamespaceForDeeplyNested(): void { // given $objectClassReaderMock = Mockery::mock(ObjectClassReader::class); - $provider = new ModelNamespaceProvider($objectClassReaderMock); + $provider = new ModelNamespaceResolver($objectClassReaderMock); $modelMock = Mockery::mock(TemplateModelInterface::class); // when - $getModelNamespace = fn()=> $provider->getModelNamespace($modelMock); + $getModelNamespace = fn()=> $provider->resolveModelNamespace($modelMock); // then $objectClassReaderMock diff --git a/tests/pest/Unit/Model/ModelRendererTest.php b/tests/pest/Unit/Model/ModelRendererTest.php index 631aa6e..04ebf8a 100644 --- a/tests/pest/Unit/Model/ModelRendererTest.php +++ b/tests/pest/Unit/Model/ModelRendererTest.php @@ -9,7 +9,7 @@ use Prosopo\Views\Interfaces\Model\ModelFactoryInterface; use Prosopo\Views\Interfaces\Model\TemplateModelInterface; use Prosopo\Views\Interfaces\Object\ObjectReaderInterface; -use Prosopo\Views\Interfaces\Template\ModelTemplateProviderInterface; +use Prosopo\Views\Interfaces\Template\ModelTemplateResolverInterface; use Prosopo\Views\Interfaces\Template\TemplateRendererInterface; use Prosopo\Views\PrivateClasses\Model\ModelRenderer; @@ -20,7 +20,7 @@ public function testRenderModelWithModelInstance(): void // given $templateRendererMock = Mockery::mock(TemplateRendererInterface::class); $modelFactoryMock = Mockery::mock(ModelFactoryInterface::class); - $templateProviderMock = Mockery::mock(ModelTemplateProviderInterface::class); + $templateProviderMock = Mockery::mock(ModelTemplateResolverInterface::class); $modelMock = Mockery::mock(TemplateModelInterface::class); $renderer = new ModelRenderer( $templateRendererMock, @@ -36,7 +36,7 @@ public function testRenderModelWithModelInstance(): void ->once() ->andReturn(['key' => 'value']); - $templateProviderMock->shouldReceive('getModelTemplate') + $templateProviderMock->shouldReceive('resolveModelTemplate') ->once() ->with($modelMock) ->andReturn('
{{ $key }}
'); @@ -58,7 +58,7 @@ public function testRenderModelWithModelClass(): void // given $templateRendererMock = Mockery::mock(TemplateRendererInterface::class); $modelFactoryMock = Mockery::mock(ModelFactoryInterface::class); - $templateProviderMock = Mockery::mock(ModelTemplateProviderInterface::class); + $templateProviderMock = Mockery::mock(ModelTemplateResolverInterface::class); $modelMock = Mockery::mock(TemplateModelInterface::class); $renderer = new ModelRenderer( $templateRendererMock, @@ -70,7 +70,7 @@ public function testRenderModelWithModelClass(): void $result = fn() => $renderer->renderModel('ModelClass'); // then - $modelFactoryMock->shouldReceive('makeModel') + $modelFactoryMock->shouldReceive('createModel') ->once() ->with('ModelClass') ->andReturn($modelMock); @@ -79,7 +79,7 @@ public function testRenderModelWithModelClass(): void ->once() ->andReturn(['key' => 'value']); - $templateProviderMock->shouldReceive('getModelTemplate') + $templateProviderMock->shouldReceive('resolveModelTemplate') ->once() ->with($modelMock) ->andReturn('
{{ $key }}
'); @@ -100,7 +100,7 @@ public function testRenderModelAppliesSetupCallback(): void // given $templateRendererMock = Mockery::mock(TemplateRendererInterface::class); $modelFactoryMock = Mockery::mock(ModelFactoryInterface::class); - $templateProviderMock = Mockery::mock(ModelTemplateProviderInterface::class); + $templateProviderMock = Mockery::mock(ModelTemplateResolverInterface::class); $modelMock = Mockery::mock(TemplateModelInterface::class); $renderer = new ModelRenderer( $templateRendererMock, @@ -115,7 +115,7 @@ public function testRenderModelAppliesSetupCallback(): void $result = fn()=>$renderer->renderModel('ModelClass', $setupCallback); // then - $modelFactoryMock->shouldReceive('makeModel') + $modelFactoryMock->shouldReceive('createModel') ->once() ->with('ModelClass') ->andReturn($modelMock); @@ -124,7 +124,7 @@ public function testRenderModelAppliesSetupCallback(): void ->once() ->andReturn(['key' => 'modified_value']); - $templateProviderMock->shouldReceive('getModelTemplate') + $templateProviderMock->shouldReceive('resolveModelTemplate') ->once() ->with($modelMock) ->andReturn('
{{ $key }}
'); @@ -145,7 +145,7 @@ public function testRenderModelPrintsOutput(): void // given $templateRendererMock = Mockery::mock(TemplateRendererInterface::class); $modelFactoryMock = Mockery::mock(ModelFactoryInterface::class); - $templateProviderMock = Mockery::mock(ModelTemplateProviderInterface::class); + $templateProviderMock = Mockery::mock(ModelTemplateResolverInterface::class); $modelMock = Mockery::mock(TemplateModelInterface::class); $renderer = new ModelRenderer( $templateRendererMock, @@ -161,7 +161,7 @@ public function testRenderModelPrintsOutput(): void ->once() ->andReturn(['key' => 'value']); - $templateProviderMock->shouldReceive('getModelTemplate') + $templateProviderMock->shouldReceive('resolveModelTemplate') ->once() ->with($modelMock) ->andReturn('
{{ $key }}
'); diff --git a/tests/pest/Unit/Model/ModelRendererWithEventDetailsTest.php b/tests/pest/Unit/Model/ModelRendererWithEventDetailsTest.php index 1fe7587..92d1488 100644 --- a/tests/pest/Unit/Model/ModelRendererWithEventDetailsTest.php +++ b/tests/pest/Unit/Model/ModelRendererWithEventDetailsTest.php @@ -25,7 +25,7 @@ public function testRenderModelAttachesAndDetachesEventDetails(): void $result = fn()=>$renderer->renderModel($modelMock); // then - $eventDispatcherMock->shouldReceive('attachEventDetails') + $eventDispatcherMock->shouldReceive('registerEventDetails') ->once() ->with('render_event', ['viewClass' => get_class($modelMock)]); @@ -34,7 +34,7 @@ public function testRenderModelAttachesAndDetachesEventDetails(): void ->with($modelMock, null, false) ->andReturn('
Rendered View
'); - $eventDispatcherMock->shouldReceive('detachEventDetails') + $eventDispatcherMock->shouldReceive('unregisterEventDetails') ->once() ->with('render_event', ['viewClass' => get_class($modelMock)]); @@ -55,7 +55,7 @@ public function testRenderModelHandlesModelClass(): void $result = fn()=> $renderer->renderModel('TestModelClass'); // then - $eventDispatcherMock->shouldReceive('attachEventDetails') + $eventDispatcherMock->shouldReceive('registerEventDetails') ->once() ->with('render_event', ['viewClass' => 'TestModelClass']); @@ -64,7 +64,7 @@ public function testRenderModelHandlesModelClass(): void ->with('TestModelClass', null, false) ->andReturn('
Rendered Model
'); - $eventDispatcherMock->shouldReceive('detachEventDetails') + $eventDispatcherMock->shouldReceive('unregisterEventDetails') ->once() ->with('render_event', ['viewClass' => 'TestModelClass']); @@ -92,7 +92,7 @@ public function testRenderModelPassesSetupCallback(): void $result = fn()=>$renderer->renderModel($modelMock, $setupCallback); // then - $eventDispatcherMock->shouldReceive('attachEventDetails') + $eventDispatcherMock->shouldReceive('registerEventDetails') ->once() ->with('render_event', ['viewClass' => get_class($modelMock)]); @@ -101,7 +101,7 @@ public function testRenderModelPassesSetupCallback(): void ->with($modelMock, $setupCallback, false) ->andReturn('
Modified View
'); - $eventDispatcherMock->shouldReceive('detachEventDetails') + $eventDispatcherMock->shouldReceive('unregisterEventDetails') ->once() ->with('render_event', ['viewClass' => get_class($modelMock)]); @@ -124,7 +124,7 @@ public function testRenderModelHandlesDoPrintFlag(): void $renderModel = fn()=>$renderer->renderModel($modelMock, null, true); // then - $eventDispatcherMock->shouldReceive('attachEventDetails') + $eventDispatcherMock->shouldReceive('registerEventDetails') ->once() ->with('render_event', ['viewClass' => get_class($modelMock)]); @@ -133,7 +133,7 @@ public function testRenderModelHandlesDoPrintFlag(): void ->with($modelMock, null, true) ->andReturn('
Printed View
'); - $eventDispatcherMock->shouldReceive('detachEventDetails') + $eventDispatcherMock->shouldReceive('unregisterEventDetails') ->once() ->with('render_event', ['viewClass' => get_class($modelMock)]); diff --git a/tests/pest/Unit/Object/ObjectPropertyWriterTest.php b/tests/pest/Unit/Object/ObjectPropertyWriterTest.php index 50815b9..61c7d95 100644 --- a/tests/pest/Unit/Object/ObjectPropertyWriterTest.php +++ b/tests/pest/Unit/Object/ObjectPropertyWriterTest.php @@ -21,7 +21,7 @@ public function testSetsDefaultValuesUsingProvider(): void $writer = new ObjectPropertyWriter(); // when - $result = fn() => $writer->setObjectPropertyValues($testInstance, $propertyValueProvider); + $result = fn() => $writer->assignPropertyValues($testInstance, $propertyValueProvider); // then $propertyValueProvider->shouldReceive('supportsProperty') @@ -51,7 +51,7 @@ public function testIgnoresInitializedProperties(): void }; // when - $result = fn() => $writer->setObjectPropertyValues($testInstance, $propertyValueProvider); + $result = fn() => $writer->assignPropertyValues($testInstance, $propertyValueProvider); // then $result(); @@ -69,7 +69,7 @@ public function testSkipsNonSupportedType(): void $writer = new ObjectPropertyWriter(); // when - $result = fn() => $writer->setObjectPropertyValues($testInstance, $propertyValueProvider); + $result = fn() => $writer->assignPropertyValues($testInstance, $propertyValueProvider); // then $propertyValueProvider->shouldReceive('supportsProperty') diff --git a/tests/pest/Unit/Object/ObjectReaderTest.php b/tests/pest/Unit/Object/ObjectReaderTest.php index 1ff978e..99d5cdf 100644 --- a/tests/pest/Unit/Object/ObjectReaderTest.php +++ b/tests/pest/Unit/Object/ObjectReaderTest.php @@ -29,7 +29,7 @@ public function calculate(): int }; // when - $variables = $objectReader->getObjectVariables($testInstance); + $variables = $objectReader->extractObjectVariables($testInstance); // then $this->assertEquals([ @@ -50,7 +50,7 @@ public function testExcludesNonTypedPublicProperties(): void }; // when - $variables = $objectReader->getObjectVariables($testInstance); + $variables = $objectReader->extractObjectVariables($testInstance); // then $this->assertEquals([ @@ -72,7 +72,7 @@ public function someMethod(): string }; // when - $variables = $objectReader->getObjectVariables($testInstance); + $variables = $objectReader->extractObjectVariables($testInstance); // then diff --git a/tests/pest/Unit/Object/PropertyValueProviderForModelsTest.php b/tests/pest/Unit/Object/PropertyValueProviderForModelsTest.php index fc0524d..daa3644 100644 --- a/tests/pest/Unit/Object/PropertyValueProviderForModelsTest.php +++ b/tests/pest/Unit/Object/PropertyValueProviderForModelsTest.php @@ -141,7 +141,7 @@ public function testGetPropertyValueCreatesModelForValidClass(): void ->with($propertyMock) ->andReturn(false); - $modelFactoryMock->shouldReceive('makeModel') + $modelFactoryMock->shouldReceive('createModel') ->once() ->with(TemplateModel::class) ->andReturn($model); diff --git a/tests/pest/Unit/Template/FileModelTemplateProviderTest.php b/tests/pest/Unit/Template/FileModelTemplateProviderTest.php index a42f72d..7c14f99 100644 --- a/tests/pest/Unit/Template/FileModelTemplateProviderTest.php +++ b/tests/pest/Unit/Template/FileModelTemplateProviderTest.php @@ -7,10 +7,10 @@ use Mockery; use org\bovigo\vfs\vfsStream; use PHPUnit\Framework\TestCase; -use Prosopo\Views\Interfaces\Model\ModelNameProviderInterface; -use Prosopo\Views\Interfaces\Model\ModelNamespaceProviderInterface; +use Prosopo\Views\Interfaces\Model\ModelNameResolverInterface; +use Prosopo\Views\Interfaces\Model\ModelNamespaceResolverInterface; use Prosopo\Views\Interfaces\Model\TemplateModelInterface; -use Prosopo\Views\PrivateClasses\Template\FileModelTemplateProvider; +use Prosopo\Views\PrivateClasses\Template\FileModelTemplateResolver; class FileModelTemplateProviderTest extends TestCase { @@ -19,9 +19,9 @@ public function testGetTemplateReturnsFileContent(): void // given vfsStream::setup('templates', null, ['sample-view.blade.php' => 'View Content']); $templateModel = Mockery::mock(TemplateModelInterface::class); - $modelNamespaceProviderMock = Mockery::mock(ModelNamespaceProviderInterface::class); - $modelNameProviderMock = Mockery::mock(ModelNameProviderInterface::class); - $provider = new FileModelTemplateProvider( + $modelNamespaceProviderMock = Mockery::mock(ModelNamespaceResolverInterface::class); + $modelNameProviderMock = Mockery::mock(ModelNameResolverInterface::class); + $provider = new FileModelTemplateResolver( 'App\\Views', vfsStream::url('templates'), '.blade.php', @@ -31,14 +31,14 @@ public function testGetTemplateReturnsFileContent(): void ); // when - $result = fn() => $provider->getModelTemplate($templateModel); + $result = fn() => $provider->resolveModelTemplate($templateModel); // then - $modelNamespaceProviderMock->shouldReceive('getModelNamespace') + $modelNamespaceProviderMock->shouldReceive('resolveModelNamespace') ->once() ->with($templateModel) ->andReturn('App\\Views'); - $modelNameProviderMock->shouldReceive('getModelName') + $modelNameProviderMock->shouldReceive('resolveModelName') ->once() ->with($templateModel) ->andReturn('SampleView'); @@ -54,9 +54,9 @@ public function testGetFileBasedTemplateReturnsFileName(): void // given vfsStream::setup('templates', null, ['sample-view.blade.php' => 'View Content']); $templateModel = Mockery::mock(TemplateModelInterface::class); - $modelNamespaceProviderMock = Mockery::mock(ModelNamespaceProviderInterface::class); - $modelNameProviderMock = Mockery::mock(ModelNameProviderInterface::class); - $provider = new FileModelTemplateProvider( + $modelNamespaceProviderMock = Mockery::mock(ModelNamespaceResolverInterface::class); + $modelNameProviderMock = Mockery::mock(ModelNameResolverInterface::class); + $provider = new FileModelTemplateResolver( 'App\\Views', vfsStream::url('templates'), '.blade.php', @@ -66,14 +66,14 @@ public function testGetFileBasedTemplateReturnsFileName(): void ); // when - $result = fn() => $provider->getModelTemplate($templateModel); + $result = fn() => $provider->resolveModelTemplate($templateModel); // then - $modelNamespaceProviderMock->shouldReceive('getModelNamespace') + $modelNamespaceProviderMock->shouldReceive('resolveModelNamespace') ->once() ->with($templateModel) ->andReturn('App\\Views'); - $modelNameProviderMock->shouldReceive('getModelName') + $modelNameProviderMock->shouldReceive('resolveModelName') ->once() ->with($templateModel) ->andReturn('SampleView'); @@ -89,9 +89,9 @@ public function testGetTemplateReturnsEmptyStringForMissingFile(): void // given vfsStream::setup('templates'); $templateModel = Mockery::mock(TemplateModelInterface::class); - $modelNamespaceProviderMock = Mockery::mock(ModelNamespaceProviderInterface::class); - $modelNameProviderMock = Mockery::mock(ModelNameProviderInterface::class); - $provider = new FileModelTemplateProvider( + $modelNamespaceProviderMock = Mockery::mock(ModelNamespaceResolverInterface::class); + $modelNameProviderMock = Mockery::mock(ModelNameResolverInterface::class); + $provider = new FileModelTemplateResolver( 'App\\Views', vfsStream::url('templates'), '.blade.php', @@ -101,14 +101,14 @@ public function testGetTemplateReturnsEmptyStringForMissingFile(): void ); // when - $result = fn() => $provider->getModelTemplate($templateModel); + $result = fn() => $provider->resolveModelTemplate($templateModel); // then - $modelNamespaceProviderMock->shouldReceive('getModelNamespace') + $modelNamespaceProviderMock->shouldReceive('resolveModelNamespace') ->once() ->with($templateModel) ->andReturn('App\\Views'); - $modelNameProviderMock->shouldReceive('getModelName') + $modelNameProviderMock->shouldReceive('resolveModelName') ->once() ->with($templateModel) ->andReturn('SampleView'); @@ -124,9 +124,9 @@ public function testGetFileBasedTemplateReturnsPathForMissingFile(): void // given vfsStream::setup('templates'); $templateModel = Mockery::mock(TemplateModelInterface::class); - $modelNamespaceProviderMock = Mockery::mock(ModelNamespaceProviderInterface::class); - $modelNameProviderMock = Mockery::mock(ModelNameProviderInterface::class); - $provider = new FileModelTemplateProvider( + $modelNamespaceProviderMock = Mockery::mock(ModelNamespaceResolverInterface::class); + $modelNameProviderMock = Mockery::mock(ModelNameResolverInterface::class); + $provider = new FileModelTemplateResolver( 'App\\Views', vfsStream::url('templates'), '.blade.php', @@ -136,14 +136,14 @@ public function testGetFileBasedTemplateReturnsPathForMissingFile(): void ); // when - $result = fn() => $provider->getModelTemplate($templateModel); + $result = fn() => $provider->resolveModelTemplate($templateModel); // then - $modelNamespaceProviderMock->shouldReceive('getModelNamespace') + $modelNamespaceProviderMock->shouldReceive('resolveModelNamespace') ->once() ->with($templateModel) ->andReturn('App\\Views'); - $modelNameProviderMock->shouldReceive('getModelName') + $modelNameProviderMock->shouldReceive('resolveModelName') ->once() ->with($templateModel) ->andReturn('SampleView'); @@ -159,9 +159,9 @@ public function testGetTemplateHandlesCamelCaseConversion(): void // given vfsStream::setup('templates', null, ['some-camel-case-view.blade.php' => 'Camel Case Content']); $templateModel = Mockery::mock(TemplateModelInterface::class); - $modelNamespaceProviderMock = Mockery::mock(ModelNamespaceProviderInterface::class); - $modelNameProviderMock = Mockery::mock(ModelNameProviderInterface::class); - $provider = new FileModelTemplateProvider( + $modelNamespaceProviderMock = Mockery::mock(ModelNamespaceResolverInterface::class); + $modelNameProviderMock = Mockery::mock(ModelNameResolverInterface::class); + $provider = new FileModelTemplateResolver( 'App\\Views', vfsStream::url('templates'), '.blade.php', @@ -171,14 +171,14 @@ public function testGetTemplateHandlesCamelCaseConversion(): void ); // when - $result = fn() => $provider->getModelTemplate($templateModel); + $result = fn() => $provider->resolveModelTemplate($templateModel); // then - $modelNamespaceProviderMock->shouldReceive('getModelNamespace') + $modelNamespaceProviderMock->shouldReceive('resolveModelNamespace') ->once() ->with($templateModel) ->andReturn('App\\Views'); - $modelNameProviderMock->shouldReceive('getModelName') + $modelNameProviderMock->shouldReceive('resolveModelName') ->once() ->with($templateModel) ->andReturn('SomeCamelCaseView'); @@ -194,9 +194,9 @@ public function testGetFileBasedTemplateHandlesCamelCaseConversion(): void // given vfsStream::setup('templates', null, ['some-camel-case-view.blade.php' => 'Camel Case Content']); $templateModel = Mockery::mock(TemplateModelInterface::class); - $modelNamespaceProviderMock = Mockery::mock(ModelNamespaceProviderInterface::class); - $modelNameProviderMock = Mockery::mock(ModelNameProviderInterface::class); - $provider = new FileModelTemplateProvider( + $modelNamespaceProviderMock = Mockery::mock(ModelNamespaceResolverInterface::class); + $modelNameProviderMock = Mockery::mock(ModelNameResolverInterface::class); + $provider = new FileModelTemplateResolver( 'App\\Views', vfsStream::url('templates'), '.blade.php', @@ -206,14 +206,14 @@ public function testGetFileBasedTemplateHandlesCamelCaseConversion(): void ); // when - $result = fn() => $provider->getModelTemplate($templateModel); + $result = fn() => $provider->resolveModelTemplate($templateModel); // then - $modelNamespaceProviderMock->shouldReceive('getModelNamespace') + $modelNamespaceProviderMock->shouldReceive('resolveModelNamespace') ->once() ->with($templateModel) ->andReturn('App\\Views'); - $modelNameProviderMock->shouldReceive('getModelName') + $modelNameProviderMock->shouldReceive('resolveModelName') ->once() ->with($templateModel) ->andReturn('SomeCamelCaseView'); @@ -229,9 +229,9 @@ public function testGetTemplateHandlesNestedNamespaces(): void // given vfsStream::setup('templates', null, ['admin/dashboard-view.blade.php' => 'Dashboard Content']); $templateModel = Mockery::mock(TemplateModelInterface::class); - $modelNamespaceProviderMock = Mockery::mock(ModelNamespaceProviderInterface::class); - $modelNameProviderMock = Mockery::mock(ModelNameProviderInterface::class); - $provider = new FileModelTemplateProvider( + $modelNamespaceProviderMock = Mockery::mock(ModelNamespaceResolverInterface::class); + $modelNameProviderMock = Mockery::mock(ModelNameResolverInterface::class); + $provider = new FileModelTemplateResolver( 'App\\Views', vfsStream::url('templates'), '.blade.php', @@ -241,14 +241,14 @@ public function testGetTemplateHandlesNestedNamespaces(): void ); // when - $result = fn() => $provider->getModelTemplate($templateModel); + $result = fn() => $provider->resolveModelTemplate($templateModel); // then - $modelNamespaceProviderMock->shouldReceive('getModelNamespace') + $modelNamespaceProviderMock->shouldReceive('resolveModelNamespace') ->once() ->with($templateModel) ->andReturn('App\\Views\\Admin'); - $modelNameProviderMock->shouldReceive('getModelName') + $modelNameProviderMock->shouldReceive('resolveModelName') ->once() ->with($templateModel) ->andReturn('DashboardView'); @@ -263,9 +263,9 @@ public function testGetFileBasedTemplateHandlesNestedNamespaces(): void // given vfsStream::setup('templates', null, ['admin/dashboard-view.blade.php' => 'Dashboard Content']); $templateModel = Mockery::mock(TemplateModelInterface::class); - $modelNamespaceProviderMock = Mockery::mock(ModelNamespaceProviderInterface::class); - $modelNameProviderMock = Mockery::mock(ModelNameProviderInterface::class); - $provider = new FileModelTemplateProvider( + $modelNamespaceProviderMock = Mockery::mock(ModelNamespaceResolverInterface::class); + $modelNameProviderMock = Mockery::mock(ModelNameResolverInterface::class); + $provider = new FileModelTemplateResolver( 'App\\Views', vfsStream::url('templates'), '.blade.php', @@ -275,14 +275,14 @@ public function testGetFileBasedTemplateHandlesNestedNamespaces(): void ); // when - $result = fn() => $provider->getModelTemplate($templateModel); + $result = fn() => $provider->resolveModelTemplate($templateModel); // then - $modelNamespaceProviderMock->shouldReceive('getModelNamespace') + $modelNamespaceProviderMock->shouldReceive('resolveModelNamespace') ->once() ->with($templateModel) ->andReturn('App\\Views\\Admin'); - $modelNameProviderMock->shouldReceive('getModelName') + $modelNameProviderMock->shouldReceive('resolveModelName') ->once() ->with($templateModel) ->andReturn('DashboardView'); diff --git a/tests/pest/Unit/Template/TemplateRendererTest.php b/tests/pest/Unit/Template/TemplateRendererTest.php index 9828218..fa42bea 100644 --- a/tests/pest/Unit/Template/TemplateRendererTest.php +++ b/tests/pest/Unit/Template/TemplateRendererTest.php @@ -6,7 +6,7 @@ use Mockery; use PHPUnit\Framework\TestCase; -use Prosopo\Views\Interfaces\CodeExecutorInterface; +use Prosopo\Views\Interfaces\CodeRunnerInterface; use Prosopo\Views\PrivateClasses\Template\TemplateRenderer; class TemplateRendererTest extends TestCase @@ -14,14 +14,14 @@ class TemplateRendererTest extends TestCase public function testRenderTemplateReturnsRenderedContent(): void { // given - $templateExecutor = Mockery::mock(CodeExecutorInterface::class); + $templateExecutor = Mockery::mock(CodeRunnerInterface::class); $renderer = new TemplateRenderer($templateExecutor); // when $result = fn() => $renderer->renderTemplate('
{{ $var }}
', ['var' => 'Test Content']); // then - $templateExecutor->shouldReceive('executeCode') + $templateExecutor->shouldReceive('runCode') ->once() ->with('
{{ $var }}
', ['var' => 'Test Content']) ->andReturnUsing(function () { @@ -36,7 +36,7 @@ public function testRenderTemplateReturnsRenderedContent(): void public function testRenderTemplateDoesNotPrintByDefault(): void { // given - $templateExecutor = Mockery::mock(CodeExecutorInterface::class); + $templateExecutor = Mockery::mock(CodeRunnerInterface::class); $renderer = new TemplateRenderer($templateExecutor); // when @@ -45,7 +45,7 @@ public function testRenderTemplateDoesNotPrintByDefault(): void $output = ob_get_clean(); // then - $templateExecutor->shouldReceive('executeCode') + $templateExecutor->shouldReceive('runCode') ->once() ->with('

{{ $message }}

', ['message' => 'Hello, World!']) ->andReturnUsing(function () { @@ -62,14 +62,14 @@ public function testRenderTemplateDoesNotPrintByDefault(): void public function testRenderTemplatePrintsWhenDoPrintIsTrue(): void { // given - $templateExecutor = Mockery::mock(CodeExecutorInterface::class); + $templateExecutor = Mockery::mock(CodeRunnerInterface::class); $renderer = new TemplateRenderer($templateExecutor); // when $result = fn() => $renderer->renderTemplate('

{{ $title }}

', ['title' => 'Welcome'], true); // then - $templateExecutor->shouldReceive('executeCode') + $templateExecutor->shouldReceive('runCode') ->once() ->with('

{{ $title }}

', ['title' => 'Welcome']) ->andReturnUsing(function () { @@ -90,14 +90,14 @@ public function testRenderTemplatePrintsWhenDoPrintIsTrue(): void public function testRenderTemplateHandlesEmptyTemplate(): void { // given - $templateExecutor = Mockery::mock(CodeExecutorInterface::class); + $templateExecutor = Mockery::mock(CodeRunnerInterface::class); $renderer = new TemplateRenderer($templateExecutor); // when $result = fn() => $renderer->renderTemplate('', []); // then - $templateExecutor->shouldReceive('executeCode') + $templateExecutor->shouldReceive('runCode') ->once() ->with('', []) ->andReturnUsing(function () { diff --git a/tests/pest/Unit/Template/TemplateRendererWithEventDetailsTest.php b/tests/pest/Unit/Template/TemplateRendererWithEventDetailsTest.php index ff3cfdb..353127e 100644 --- a/tests/pest/Unit/Template/TemplateRendererWithEventDetailsTest.php +++ b/tests/pest/Unit/Template/TemplateRendererWithEventDetailsTest.php @@ -23,7 +23,7 @@ public function testRenderTemplateAttachesAndDetachesEventDetails(): void $result = fn() => $renderer->renderTemplate('
{{ $var }}
', ['var' => 'Test Content']); // then - $eventDispatcherMock->shouldReceive('attachEventDetails') + $eventDispatcherMock->shouldReceive('registerEventDetails') ->once() ->with('render_event', ['template' => '
{{ $var }}
',]); @@ -32,7 +32,7 @@ public function testRenderTemplateAttachesAndDetachesEventDetails(): void ->with('
{{ $var }}
', ['var' => 'Test Content'], false) ->andReturn('
Test Content
'); - $eventDispatcherMock->shouldReceive('detachEventDetails') + $eventDispatcherMock->shouldReceive('unregisterEventDetails') ->once() ->with('render_event', ['template' => '
{{ $var }}
']); @@ -57,7 +57,7 @@ public function testRenderTemplatePassesDoPrintFlag(): void ); // then - $eventDispatcherMock->shouldReceive('attachEventDetails') + $eventDispatcherMock->shouldReceive('registerEventDetails') ->once() ->with('render_event', ['template' => '

{{ $message }}

']); @@ -66,7 +66,7 @@ public function testRenderTemplatePassesDoPrintFlag(): void ->with('

{{ $message }}

', ['message' => 'Hello, World!'], true) ->andReturn('

Hello, World!

'); - $eventDispatcherMock->shouldReceive('detachEventDetails') + $eventDispatcherMock->shouldReceive('unregisterEventDetails') ->once() ->with('render_event', ['template' => '

{{ $message }}

']); @@ -87,7 +87,7 @@ public function testRenderTemplateHandlesEmptyTemplate(): void $result = fn() => $renderer->renderTemplate('', []); // then - $eventDispatcherMock->shouldReceive('attachEventDetails') + $eventDispatcherMock->shouldReceive('registerEventDetails') ->once() ->with('render_event', ['template' => '']); @@ -96,7 +96,7 @@ public function testRenderTemplateHandlesEmptyTemplate(): void ->with('', [], false) ->andReturn(''); - $eventDispatcherMock->shouldReceive('detachEventDetails') + $eventDispatcherMock->shouldReceive('unregisterEventDetails') ->once() ->with('render_event', ['template' => '']); diff --git a/tests/pest/Unit/View/ViewNamespaceContainerTest.php b/tests/pest/Unit/View/ViewNamespaceContainerTest.php index 7dde988..5a1d333 100644 --- a/tests/pest/Unit/View/ViewNamespaceContainerTest.php +++ b/tests/pest/Unit/View/ViewNamespaceContainerTest.php @@ -20,10 +20,10 @@ public function testGetModulesByModelNamespace(): void $container = new ViewNamespaceModulesContainer(); // when - $container->addNamespaceModules('App\\Package\\Specific', $viewNamespaceModules); + $container->registerNamespaceModules('App\\Package\\Specific', $viewNamespaceModules); // then - $resolvedModules = $container->getNamespaceModulesByModelNamespace('App\\Package\\Specific\\Model'); + $resolvedModules = $container->resolveNamespaceModules('App\\Package\\Specific\\Model'); $this->assertSame( $viewNamespaceModules, @@ -42,11 +42,11 @@ public function testGetModulesByModelNamespacePrefersLongerNamespace(): void $container = new ViewNamespaceModulesContainer(); // when - $container->addNamespaceModules('App\\Package', $shortNamespaceModules); - $container->addNamespaceModules('App\\Package\\Specific', $longNamespaceModules); + $container->registerNamespaceModules('App\\Package', $shortNamespaceModules); + $container->registerNamespaceModules('App\\Package\\Specific', $longNamespaceModules); // then - $resolvedModules = $container->getNamespaceModulesByModelNamespace('App\\Package\\Specific\\Model'); + $resolvedModules = $container->resolveNamespaceModules('App\\Package\\Specific\\Model'); $this->assertSame( $longNamespaceModules, @@ -64,10 +64,10 @@ public function testGetModulesByModelNamespaceReturnsNullWhenNotFound(): void $container = new ViewNamespaceModulesContainer(); // when - $container->addNamespaceModules('App\\Package\\Specific', $viewNamespaceModules); + $container->registerNamespaceModules('App\\Package\\Specific', $viewNamespaceModules); // then - $resolvedModules = $container->getNamespaceModulesByModelNamespace('Another\\Package'); + $resolvedModules = $container->resolveNamespaceModules('Another\\Package'); $this->assertNull( $resolvedModules diff --git a/tests/pest/Unit/View/ViewNamespaceTest.php b/tests/pest/Unit/View/ViewNamespaceTest.php index abbd65e..13f93f1 100644 --- a/tests/pest/Unit/View/ViewNamespaceTest.php +++ b/tests/pest/Unit/View/ViewNamespaceTest.php @@ -8,14 +8,14 @@ use PHPUnit\Framework\TestCase; use Prosopo\Views\Interfaces\EventDispatcherInterface; use Prosopo\Views\Interfaces\Model\ModelFactoryInterface; -use Prosopo\Views\Interfaces\Model\ModelNameProviderInterface; -use Prosopo\Views\Interfaces\Model\ModelNamespaceProviderInterface; +use Prosopo\Views\Interfaces\Model\ModelNameResolverInterface; +use Prosopo\Views\Interfaces\Model\ModelNamespaceResolverInterface; use Prosopo\Views\Interfaces\Model\ModelRendererInterface; use Prosopo\Views\Interfaces\Model\TemplateModelInterface; use Prosopo\Views\Interfaces\Object\ObjectPropertyWriterInterface; use Prosopo\Views\Interfaces\Object\ObjectReaderInterface; use Prosopo\Views\Interfaces\Object\PropertyValueProviderInterface; -use Prosopo\Views\Interfaces\Template\ModelTemplateProviderInterface; +use Prosopo\Views\Interfaces\Template\ModelTemplateResolverInterface; use Prosopo\Views\Interfaces\Template\TemplateRendererInterface; use Prosopo\Views\PrivateClasses\View\ViewNamespace; use Prosopo\Views\View\ViewNamespaceConfig; @@ -72,7 +72,7 @@ public function testMakesModelTemplateProvider() $this->testUsesGivenModule( null, function (ViewNamespaceModules $modules) { - $this->assertNotNull($modules->getModelTemplateProvider()); + $this->assertNotNull($modules->getModelTemplateResolver()); } ); } @@ -102,7 +102,7 @@ public function testMakesModelNameProvider() $this->testUsesGivenModule( null, function (ViewNamespaceModules $modules) { - $this->assertNotNull($modules->getModelNameProvider()); + $this->assertNotNull($modules->getModelNameResolver()); } ); } @@ -112,7 +112,7 @@ public function testMakesModelNamespaceProvider() $this->testUsesGivenModule( null, function (ViewNamespaceModules $modules) { - $this->assertNotNull($modules->getModelNamespaceProvider()); + $this->assertNotNull($modules->getModelNamespaceResolver()); } ); } @@ -158,12 +158,12 @@ function (ViewNamespaceModules $modules) use ($module) { function (ViewNamespaceModules $modules) { $instance = Mockery::mock(StdClass::class); - $modules->getObjectReader()->getObjectVariables($instance); + $modules->getObjectReader()->extractObjectVariables($instance); } ); // then - $module->shouldReceive('getObjectVariables') + $module->shouldReceive('extractObjectVariables') ->once() ->andReturn([]); @@ -217,12 +217,12 @@ function (ViewNamespaceModules $modules) { $instance = Mockery::mock(StdClass::class); $propertyValueProvider = Mockery::mock(PropertyValueProviderInterface::class); - $modules->getObjectPropertyWriter()->setObjectPropertyValues($instance, $propertyValueProvider); + $modules->getObjectPropertyWriter()->assignPropertyValues($instance, $propertyValueProvider); } ); // then - $module->shouldReceive('setObjectPropertyValues') + $module->shouldReceive('assignPropertyValues') ->once(); // apply @@ -242,12 +242,12 @@ function (ViewNamespaceModules $modules) use ($module) { $modules->setModelFactory($module); }, function (ViewNamespaceModules $modules) { - $modules->getModelFactory()->makeModel('test'); + $modules->getModelFactory()->createModel('test'); } ); // then - $module->shouldReceive('makeModel') + $module->shouldReceive('createModel') ->once() ->andReturn(null); @@ -260,22 +260,22 @@ function (ViewNamespaceModules $modules) { public function testUsesGivenModelTemplateProvider(): void { // given - $module = Mockery::mock(ModelTemplateProviderInterface::class); + $module = Mockery::mock(ModelTemplateResolverInterface::class); // when $test = fn()=>$this->testUsesGivenModule( function (ViewNamespaceModules $modules) use ($module) { - $modules->setModelTemplateProvider($module); + $modules->setModelTemplateResolver($module); }, function (ViewNamespaceModules $modules) { $model = Mockery::mock(TemplateModelInterface::class); - $modules->getModelTemplateProvider()->getModelTemplate($model); + $modules->getModelTemplateResolver()->resolveModelTemplate($model); } ); // then - $module->shouldReceive('getModelTemplate') + $module->shouldReceive('resolveModelTemplate') ->once() ->andReturn(''); @@ -324,12 +324,12 @@ function (ViewNamespaceModules $modules) use ($module) { $modules->setEventDispatcher($module); }, function (ViewNamespaceModules $modules) { - $modules->getEventDispatcher()->attachEventDetails('test', []); + $modules->getEventDispatcher()->registerEventDetails('test', []); } ); // then - $module->shouldReceive('attachEventDetails') + $module->shouldReceive('registerEventDetails') ->once(); // apply @@ -341,22 +341,22 @@ function (ViewNamespaceModules $modules) { public function testUsesGivenModelNameProvider(): void { // given - $module = Mockery::mock(ModelNameProviderInterface::class); + $module = Mockery::mock(ModelNameResolverInterface::class); // when $test = fn()=>$this->testUsesGivenModule( function (ViewNamespaceModules $modules) use ($module) { - $modules->setModelNameProvider($module); + $modules->setModelNameResolver($module); }, function (ViewNamespaceModules $modules) { $model = Mockery::mock(TemplateModelInterface::class); - $modules->getModelNameProvider()->getModelName($model); + $modules->getModelNameResolver()->resolveModelName($model); } ); // then - $module->shouldReceive('getModelName') + $module->shouldReceive('resolveModelName') ->once(); // apply @@ -368,20 +368,20 @@ function (ViewNamespaceModules $modules) { public function testUsesGivenModelNamespaceProvider(): void { // given - $module = Mockery::mock(ModelNamespaceProviderInterface::class); + $module = Mockery::mock(ModelNamespaceResolverInterface::class); // when $test = fn()=>$this->testUsesGivenModule( function (ViewNamespaceModules $modules) use ($module) { - $modules->setModelNamespaceProvider($module); + $modules->setModelNamespaceResolver($module); }, function (ViewNamespaceModules $modules) { - $modules->getModelNamespaceProvider()->getModelNamespace('test'); + $modules->getModelNamespaceResolver()->resolveModelNamespace('test'); } ); // then - $module->shouldReceive('getModelNamespace') + $module->shouldReceive('resolveModelNamespace') ->once(); // apply diff --git a/tests/pest/Unit/View/ViewTemplateRendererTest.php b/tests/pest/Unit/View/ViewTemplateRendererTest.php index 125b6a8..e77b098 100644 --- a/tests/pest/Unit/View/ViewTemplateRendererTest.php +++ b/tests/pest/Unit/View/ViewTemplateRendererTest.php @@ -6,7 +6,7 @@ use Mockery; use PHPUnit\Framework\TestCase; -use Prosopo\Views\Interfaces\CodeExecutorInterface; +use Prosopo\Views\Interfaces\CodeRunnerInterface; use Prosopo\Views\Interfaces\EventDispatcherInterface; use Prosopo\Views\Interfaces\Template\TemplateCompilerInterface; use Prosopo\Views\Interfaces\Template\TemplateRendererInterface; @@ -85,12 +85,12 @@ function (ViewTemplateRendererModules $modules) use ($module) { $modules->setEventDispatcher($module); }, function (ViewTemplateRendererModules $modules) { - $modules->getEventDispatcher()->attachEventDetails('test', []); + $modules->getEventDispatcher()->registerEventDetails('test', []); } ); // then - $module->shouldReceive('attachEventDetails') + $module->shouldReceive('registerEventDetails') ->once(); // apply @@ -127,7 +127,7 @@ function (ViewTemplateRendererModules $modules) { public function testUsesGivenCodeExecutor(): void { // given - $module = Mockery::mock(CodeExecutorInterface::class); + $module = Mockery::mock(CodeRunnerInterface::class); // when $test = fn()=>$this->testUsesGivenModule( @@ -135,12 +135,12 @@ function (ViewTemplateRendererModules $modules) use ($module) { $modules->setCodeExecutor($module); }, function (ViewTemplateRendererModules $modules) { - $modules->getCodeExecutor()->executeCode('test'); + $modules->getCodeExecutor()->runCode('test'); } ); // then - $module->shouldReceive('executeCode') + $module->shouldReceive('runCode') ->once(); // apply diff --git a/tests/pest/Unit/ViewsTest.php b/tests/pest/Unit/ViewsTest.php index f46d5fc..c37f516 100644 --- a/tests/pest/Unit/ViewsTest.php +++ b/tests/pest/Unit/ViewsTest.php @@ -16,7 +16,7 @@ public function testMakeModelThrowsExceptionForMissingClass(): void $views = new Views(); // when - $makeModel = fn()=> $views->makeModel('MissingClass'); + $makeModel = fn()=> $views->createModel('MissingClass'); // then $this->expectException(Exception::class); @@ -31,7 +31,7 @@ public function testMakeModelThrowsExceptionForClassThatNotImplementTemplateMode $views = new Views(); // when - $makeModel = fn()=> $views->makeModel(get_class($this)); + $makeModel = fn()=> $views->createModel(get_class($this)); // then $this->expectException(Exception::class); From 9c59d2d732d328f88e3effc19a45dcee22264e52 Mon Sep 17 00:00:00 2001 From: Maxim Akimov Date: Wed, 18 Dec 2024 10:19:01 +0200 Subject: [PATCH 09/13] publish preparation: name improvements --- README.md | 73 ++++++++++--------- benchmark/src/Benchmark.php | 4 +- .../ModelFactoryWithDefaultsManagement.php | 2 +- .../Object/PropertyValueProviderForModels.php | 8 +- ...emplateModel.php => BaseTemplateModel.php} | 4 +- .../TemplateModelWithDefaultsInterface.php | 2 +- src/{Views.php => ViewsManager.php} | 6 +- ...ViewsConfig.php => ViewsManagerConfig.php} | 2 +- .../{ViewsTest.php => ViewsManagerTest.php} | 42 +++++------ ...ModelFactoryWithDefaultsManagementTest.php | 10 +-- .../PropertyValueProviderForModelsTest.php | 10 +-- .../{ViewsTest.php => ViewsManagerTest.php} | 14 ++-- tmp.php | 32 ++++++++ 13 files changed, 121 insertions(+), 88 deletions(-) rename src/{TemplateModel.php => BaseTemplateModel.php} (92%) rename src/{Views.php => ViewsManager.php} (96%) rename src/{ViewsConfig.php => ViewsManagerConfig.php} (98%) rename tests/pest/Feature/{ViewsTest.php => ViewsManagerTest.php} (96%) rename tests/pest/Unit/{ViewsTest.php => ViewsManagerTest.php} (86%) create mode 100644 tmp.php diff --git a/README.md b/README.md index 3d794e6..fee0505 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ You're free to use the package in your own way: ## Table of Contents - [1. Model-driven approach](#1-model-driven-approach) -- [2. Views](#2-views) +- [2. Views Manager](#2-views-manager) - [3. View Renderer](#3-view-renderer) - [4. Benchmark](#4-benchmark) - [5. Contribution](#4-contribution) @@ -43,10 +43,10 @@ Model class: ```php namespace MyPackage\Views; +use Prosopo\Views\BaseTemplateModel; use Prosopo\Views\Interfaces\Model\TemplateModelInterface; -use Prosopo\Views\TemplateModel; -class EmployeeTemplateModel extends TemplateModel +class EmployeeTemplateModel extends BaseTemplateModel { public int $salary; public int $bonus; @@ -87,7 +87,7 @@ from which {{ $salary }} is a salary, and {{ $bonus }} is a bonus. maintain flexibility and avoid specifying the exact component. -The `TemplateModel` class implements the `TemplateModelInterface`. During rendering, any inner objects that also +The `BaseTemplateModel` class implements the `TemplateModelInterface`. During rendering, any inner objects that also implement `TemplateModelInterface` will be automatically rendered and passed into the template as strings. @@ -100,15 +100,16 @@ set custom default values, consider using one of the following approaches: ```php namespace MyPackage\Views; -use Prosopo\Views\TemplateModel; +use Prosopo\Views\BaseTemplateModel; -class EmployeeTemplateModel extends TemplateModel +class EmployeeTemplateModel extends BaseTemplateModel { // approach for plain field types. public int $varWithCustomDefaultValue = 'custom default value'; public Company $company; - protected function setCustomDefaults(){ + protected function setCustomDefaults(): void + { // approach for object field types. $this->company = new Company(); } @@ -131,19 +132,21 @@ namespace MyPackage\Views; use Prosopo\Views\Interfaces\Model\TemplateModelInterface; -class AnyClass implements TemplateModelInterface { - public function getTemplateArguments(): array { +class AnyClass implements TemplateModelInterface +{ + public function getTemplateArguments(): array + { // you can fill out arguments from any source or define manually. return [ - 'name' => 'value', + 'name' => 'value', ]; - } + } } ``` -## 2. Views +## 2. Views Manager -The `Views` class provides the `registerNamespace`, `createModel` and `renderModel` methods. It acts as a +The `ViewsManager` class provides the `registerNamespace`, `createModel` and `renderModel` methods. It acts as a namespace manager and brings together different namespace configurations. Each `ViewNamespace` has its own independent setup and set of modules. E.g. among these modules is the @@ -155,7 +158,7 @@ corresponding templates. ```php use Prosopo\Views\View\ViewNamespaceConfig; use Prosopo\Views\View\ViewTemplateRenderer; -use Prosopo\Views\Views; +use Prosopo\Views\ViewsManager; // 1. Make the Template Renderer. // (By default it uses the built-in Blade, but you can connect any) @@ -179,11 +182,11 @@ $namespaceConfig->getModules() // 3. Make the Views instance: -$views = new Views(); +$viewsManager = new ViewsManager(); // 4. Add the root namespace of your Template Models -$views->registerNamespace('MyPackage\Views', $namespaceConfig); +$viewsManager->registerNamespace('MyPackage\Views', $namespaceConfig); // Tip: you can have multiple namespaces, and mix their Models. ``` @@ -194,7 +197,7 @@ You can create, set values, and render a Model in a single step using the callba as shown below: ```php -echo $views->renderModel( +echo $viewsManager->renderModel( EmployeeModel::class, function (EmployeeModel $employee) use ($salary, $bonus) { $employee->salary = $salary; @@ -210,7 +213,7 @@ This approach enables a functional programming style when working with Models. When you need split creation, use the factory to create the model, and then render later when you need it. ```php -$employee = $views->createModel(EmployeeModel::class); +$employee = $viewsManager->createModel(EmployeeModel::class); // ... @@ -225,15 +228,13 @@ echo $views->renderModel($employee); // to customize the Model properties before rendering. ``` -Advice: The `Views` class implements three interfaces: `ViewNamespaceManagerInterface` (for `registerNamespace`), -`ModelFactoryInterface` (for -`createModel`), and `ModelRendererInterface` (for `renderModel`). +Advice: The `ViewsManager` class implements three interfaces: `ViewNamespaceManagerInterface` (for `registerNamespace`), +`ModelFactoryInterface` (for `createModel`), and `ModelRendererInterface` (for `renderModel`). -When passing the `Views` instance to your methods, use -one of these interfaces as the argument type instead of the `Views` class itself. +When passing the `ViewsManager` instance to your methods, use one of these interfaces as the argument type instead of +the `ViewsManager` class itself. -This approach ensures that only the specific actions -you expect are accessible, promoting cleaner and more maintainable code. +This approach ensures that only the specific actions you expect are accessible, promoting cleaner and more maintainable code. ### 2.4) Automated templates matching @@ -261,7 +262,8 @@ names. ### 2.5) Custom modules -By default, the `registerNamespace` class creates module instances for the namespace using classes from the current package. +By default, the `registerNamespace` class creates module instances for the namespace using classes from the current +package. If you need to override the default module behavior, you can define a custom implementation in the configuration and the package will use the specified implementation. @@ -275,7 +277,7 @@ configuration and the package will use the specified implementation. use Prosopo\Views\Interfaces\Template\TemplateRendererInterface; use Prosopo\Views\View\ViewNamespaceConfig; -use Prosopo\Views\Views; +use Prosopo\Views\ViewsManager; class TwigDecorator implements TemplateRendererInterface { @@ -286,9 +288,9 @@ class TwigDecorator implements TemplateRendererInterface // todo init Twig or another engine. } - public function renderTemplate(string $template, array $variables, bool $doPrint = false): string + public function renderTemplate(string $template, array $variables = []): string { - return $this->twig->render($template, $variables, $doPrint); + return $this->twig->render($template, $variables); } } @@ -302,11 +304,11 @@ $namespaceConfig = (new ViewNamespaceConfig($twigDecorator)) // 3. Make the Views: -$views = new Views(); +$viewsManager = new ViewsManager(); // 4. Add the namespace (you can have multiple namespaces) -$views->registerNamespace('MyPackage\Views', $namespaceConfig); +$viewsManager->registerNamespace('MyPackage\Views', $namespaceConfig); ``` You can override any namespace module in the following way: @@ -322,7 +324,7 @@ $namespaceConfig->getModules() ### 2.6) Namespace mixing -> Fun Fact: The `Views` class not only supporting multiple namespaces, but also enabling you to use Models from one +> Fun Fact: The `ViewsManager` class not only supporting multiple namespaces, but also enabling you to use Models from one > namespace within another, preserving their individual setup. Example of multi-namespace usage: @@ -414,8 +416,7 @@ echo $viewTemplateRenderer->renderTemplate('@if($var)The variable is set.@endif' ### 3.3) Available View Renderer settings The `ViewTemplateRenderer` supports a variety of settings that let you customize features such as -escaping, -error handling, and more: +escaping, error handling, and more: ```php use Prosopo\Views\View\ViewTemplateRenderer; @@ -471,7 +472,7 @@ use Prosopo\Views\Interfaces\Template\TemplateCompilerInterface; use Prosopo\Views\View\ViewNamespaceConfig; use Prosopo\Views\View\ViewTemplateRenderer; use Prosopo\Views\View\ViewTemplateRendererConfig; -use Prosopo\Views\Views; +use Prosopo\Views\ViewsManager; class CompilerStubForPlainPhpSupport implements TemplateCompilerInterface { @@ -489,7 +490,7 @@ $viewTemplateRendererConfig->getModules() $viewTemplateRenderer = new ViewTemplateRenderer($viewTemplateRendererConfig); -$views = new Views(); +$views = new ViewsManager(); $viewNamespaceConfig = new ViewNamespaceConfig($viewTemplateRenderer); $viewNamespaceConfig diff --git a/benchmark/src/Benchmark.php b/benchmark/src/Benchmark.php index 2739ee7..0e40b43 100644 --- a/benchmark/src/Benchmark.php +++ b/benchmark/src/Benchmark.php @@ -8,7 +8,7 @@ use Jenssegers\Blade\Blade; use Prosopo\Views\View\ViewNamespaceConfig; use Prosopo\Views\View\ViewTemplateRenderer; -use Prosopo\Views\Views; +use Prosopo\Views\ViewsManager; use Twig\Environment; use Twig\Loader\FilesystemLoader; @@ -170,7 +170,7 @@ protected function benchmarkForPhpViewsWithModels( mkdir($phpViewsWithModelsDir); $phpViewsRenderer = new ViewTemplateRenderer(); - $phpViews = new Views(); + $phpViews = new ViewsManager(); $namespaceConfig = new ViewNamespaceConfig($phpViewsRenderer); $namespaceConfig->setTemplateFileExtension('.blade.php'); diff --git a/private-classes/Model/ModelFactoryWithDefaultsManagement.php b/private-classes/Model/ModelFactoryWithDefaultsManagement.php index 7669436..08fe05a 100644 --- a/private-classes/Model/ModelFactoryWithDefaultsManagement.php +++ b/private-classes/Model/ModelFactoryWithDefaultsManagement.php @@ -42,7 +42,7 @@ public function createModel(string $modelClass) protected function setDefaultValuesRecursively(TemplateModelWithDefaultsInterface $modelWithDefaults): void { - $defaultsPropertyValueProvider = $modelWithDefaults->getDefaultsProvider(); + $defaultsPropertyValueProvider = $modelWithDefaults->getDefaultPropertyValueProvider(); $this->objectPropertyWriter->assignPropertyValues($modelWithDefaults, $defaultsPropertyValueProvider); diff --git a/private-classes/Object/PropertyValueProviderForModels.php b/private-classes/Object/PropertyValueProviderForModels.php index 8792550..2758bb9 100644 --- a/private-classes/Object/PropertyValueProviderForModels.php +++ b/private-classes/Object/PropertyValueProviderForModels.php @@ -6,7 +6,7 @@ use Prosopo\Views\Interfaces\Model\ModelFactoryInterface; use Prosopo\Views\Interfaces\Object\PropertyValueProviderInterface; -use Prosopo\Views\TemplateModel; +use Prosopo\Views\BaseTemplateModel; use ReflectionProperty; /** @@ -52,14 +52,14 @@ public function getPropertyValue(ReflectionProperty $property) } /** - * @param class-string|string $propertyType + * @param class-string|string $propertyType * - * @return class-string|null + * @return class-string|null */ protected function getModelClassStringForInheritors(string $propertyType) { return true === class_exists($propertyType) && - true === is_a($propertyType, TemplateModel::class, true) ? + true === is_a($propertyType, BaseTemplateModel::class, true) ? $propertyType : null; } diff --git a/src/TemplateModel.php b/src/BaseTemplateModel.php similarity index 92% rename from src/TemplateModel.php rename to src/BaseTemplateModel.php index 9825b2f..d628ce9 100644 --- a/src/TemplateModel.php +++ b/src/BaseTemplateModel.php @@ -9,7 +9,7 @@ use Prosopo\Views\Interfaces\Object\ObjectReaderInterface; use Prosopo\Views\Interfaces\Object\PropertyValueProviderInterface; -abstract class TemplateModel implements TemplateModelInterface, TemplateModelWithDefaultsInterface +abstract class BaseTemplateModel implements TemplateModelInterface, TemplateModelWithDefaultsInterface { private ObjectReaderInterface $objectReader; private PropertyValueProviderInterface $propertyValueProviderForDefaults; @@ -50,7 +50,7 @@ public function getTemplateArguments(): array return $this->objectReader->extractObjectVariables($this); } - public function getDefaultsProvider(): PropertyValueProviderInterface + public function getDefaultPropertyValueProvider(): PropertyValueProviderInterface { return $this->propertyValueProviderForDefaults; } diff --git a/src/Interfaces/Model/TemplateModelWithDefaultsInterface.php b/src/Interfaces/Model/TemplateModelWithDefaultsInterface.php index ced9d9d..c7cd168 100644 --- a/src/Interfaces/Model/TemplateModelWithDefaultsInterface.php +++ b/src/Interfaces/Model/TemplateModelWithDefaultsInterface.php @@ -8,5 +8,5 @@ interface TemplateModelWithDefaultsInterface { - public function getDefaultsProvider(): PropertyValueProviderInterface; + public function getDefaultPropertyValueProvider(): PropertyValueProviderInterface; } diff --git a/src/Views.php b/src/ViewsManager.php similarity index 96% rename from src/Views.php rename to src/ViewsManager.php index 01b9f4b..eab1a7e 100644 --- a/src/Views.php +++ b/src/ViewsManager.php @@ -23,17 +23,17 @@ * This class is marked as a final to prevent anyone from extending it. * We reserve the right to change its private and protected methods, properties and introduce new public ones. */ -final class Views implements ViewNamespaceManagerInterface, ModelFactoryInterface, ModelRendererInterface +final class ViewsManager implements ViewNamespaceManagerInterface, ModelFactoryInterface, ModelRendererInterface { private string $namespaceNotFoundErrorMessage; private string $wrongModelErrorMessage; private ModelNamespaceResolverInterface $modelNamespaceProvider; private ViewNamespaceModulesContainerInterface $namespaceModulesContainer; - public function __construct(?ViewsConfig $config = null) + public function __construct(?ViewsManagerConfig $config = null) { $config = null === $config ? - new ViewsConfig() : + new ViewsManagerConfig() : $config; $modelNamespaceProvider = $config->getModelNamespaceProvider(); diff --git a/src/ViewsConfig.php b/src/ViewsManagerConfig.php similarity index 98% rename from src/ViewsConfig.php rename to src/ViewsManagerConfig.php index 25661bc..d8e7bed 100644 --- a/src/ViewsConfig.php +++ b/src/ViewsManagerConfig.php @@ -11,7 +11,7 @@ * This class is marked as a final to prevent anyone from extending it. * We reserve the right to change its private and protected methods, properties and introduce new public ones. */ -final class ViewsConfig +final class ViewsManagerConfig { private string $namespaceNotFoundErrorMessage; private string $wrongModelErrorMessage; diff --git a/tests/pest/Feature/ViewsTest.php b/tests/pest/Feature/ViewsManagerTest.php similarity index 96% rename from tests/pest/Feature/ViewsTest.php rename to tests/pest/Feature/ViewsManagerTest.php index 35f64be..422e44c 100644 --- a/tests/pest/Feature/ViewsTest.php +++ b/tests/pest/Feature/ViewsManagerTest.php @@ -9,13 +9,13 @@ use PHPUnit\Framework\TestCase; use Prosopo\Views\Interfaces\Model\TemplateModelInterface; use Prosopo\Views\Interfaces\Template\TemplateCompilerInterface; -use Prosopo\Views\TemplateModel; +use Prosopo\Views\BaseTemplateModel; use Prosopo\Views\View\ViewNamespaceConfig; use Prosopo\Views\View\ViewTemplateRenderer; use Prosopo\Views\View\ViewTemplateRendererConfig; -use Prosopo\Views\Views; +use Prosopo\Views\ViewsManager; -class ViewsTest extends TestCase +class ViewsManagerTest extends TestCase { //// renderModel @@ -40,7 +40,7 @@ public function testRenderModelThatImplementsInterface(): void ], false ); - $views = new Views(); + $views = new ViewsManager(); // when $views->registerNamespace($modelNamespace, $namespaceConfig); @@ -80,7 +80,7 @@ public function testRenderModelThatImplementInterfaceIgnoresPublicProperties(): false, ['data',] ); - $views = new Views(); + $views = new ViewsManager(); // when $views->registerNamespace($modelNamespace, $namespaceConfig); @@ -100,7 +100,7 @@ public function testRenderModelThatExtendsBaseClass(): void $namespaceConfig = (new ViewNamespaceConfig($bladeRenderer)) ->setTemplatesRootPath(vfsStream::url('templates')) ->setTemplateFileExtension('.blade.php'); - $views = new Views(); + $views = new ViewsManager(); $modelNamespace = $this->defineRealModelClass( __METHOD__, @@ -134,7 +134,7 @@ public function testRenderCallsSetupCallback(): void $namespaceConfig = (new ViewNamespaceConfig($bladeRenderer)) ->setTemplatesRootPath(vfsStream::url('templates')) ->setTemplateFileExtension('.blade.php'); - $views = new Views(); + $views = new ViewsManager(); $modelNamespace = $this->defineRealModelClass( __METHOD__, @@ -184,7 +184,7 @@ public function testRenderModelPassesStringToTemplateRendererWhenFileBasedModeIs ], false ); - $views = new Views(); + $views = new ViewsManager(); // when $views->registerNamespace($modelNamespace, $namespaceConfig); @@ -211,7 +211,7 @@ public function testRenderCallsTemplateErrorHandler(): void $namespaceConfig ->getModules() ->setEventDispatcher($bladeRenderer->getModules()->getEventDispatcher()); - $views = new Views(); + $views = new ViewsManager(); $modelNamespace = $this->defineRealModelClass( __METHOD__, @@ -258,7 +258,7 @@ public function testRenderNotCallTemplateErrorHandlerWithoutReason(): void $namespaceConfig ->getModules() ->setEventDispatcher($bladeRenderer->getModules()->getEventDispatcher()); - $views = new Views(); + $views = new ViewsManager(); $modelNamespace = $this->defineRealModelClass( __METHOD__, @@ -311,7 +311,7 @@ public function testRenderSupportsInnerNamespaces(): void false ); $namespace = str_replace('\inner', '', $innerModelNamespace); - $views = new Views(); + $views = new ViewsManager(); // when $views->registerNamespace($namespace, $namespaceConfig); @@ -363,7 +363,7 @@ public function testRenderSupportsDifferentNamespaces(): void ], true ); - $views = new Views(); + $views = new ViewsManager(); // when $views->registerNamespace($firstNamespace, $firstNamespaceConfig); @@ -406,7 +406,7 @@ public function testRenderIncludesInnerModel(): void ], false ); - $views = new Views(); + $views = new ViewsManager(); // when $views->registerNamespace($modelNamespace, $namespaceConfig); @@ -436,7 +436,7 @@ public function compileTemplate(string $template): string $viewTemplateRendererConfig->getModules() ->setTemplateCompiler($compilerStub); $viewTemplateRenderer = new ViewTemplateRenderer($viewTemplateRendererConfig); - $views = new Views(); + $views = new ViewsManager(); $viewNamespaceConfig = new ViewNamespaceConfig($viewTemplateRenderer); $modelNamespace = $this->defineRealModelClass( __METHOD__, @@ -496,7 +496,7 @@ public function testRenderIncludesInnerModelFromDifferentNamespace(): void ], false ); - $views = new Views(); + $views = new ViewsManager(); // when $views->registerNamespace($firstNamespace, $firstNamespaceConfig); @@ -518,7 +518,7 @@ public function testMakeModelThatExtendsBaseClass(): void // given $bladeRenderer = new ViewTemplateRenderer(); $namespaceConfig = (new ViewNamespaceConfig($bladeRenderer)); - $views = new Views(); + $views = new ViewsManager(); $modelNamespace = $this->defineRealModelClass( __METHOD__, @@ -542,7 +542,7 @@ public function testMakeModelImplementsInterface(): void // given $bladeRenderer = new ViewTemplateRenderer(); $namespaceConfig = (new ViewNamespaceConfig($bladeRenderer)); - $views = new Views(); + $views = new ViewsManager(); $modelNamespace = $this->defineRealModelClass( __METHOD__, @@ -572,7 +572,7 @@ public function testMakeModelSetsDefaultsForModelsThatExtendBaseClass(): void // given $bladeRenderer = new ViewTemplateRenderer(); $namespaceConfig = (new ViewNamespaceConfig($bladeRenderer)); - $views = new Views(); + $views = new ViewsManager(); $modelNamespace = $this->defineRealModelClass( __METHOD__, @@ -602,7 +602,7 @@ public function testMakeModelNotSetDefaultsForModelsWithoutDefaultsInterface(): // given $bladeRenderer = new ViewTemplateRenderer(); $namespaceConfig = (new ViewNamespaceConfig($bladeRenderer)); - $views = new Views(); + $views = new ViewsManager(); $modelNamespace = $this->defineRealModelClass( __METHOD__, @@ -645,7 +645,7 @@ public function testMakeModelSupportsDifferentNamespaces(): void [], true ); - $views = new Views(); + $views = new ViewsManager(); // when $views->registerNamespace($firstNamespace, $firstNamespaceConfig); @@ -676,7 +676,7 @@ protected function defineRealModelClass( $namespace = '_views_test_' . strtolower($methodName); $classContent = $this->getClassProperties($properties); $extends = true === $extendsClass ? - 'extends \\' . TemplateModel::class : + 'extends \\' . BaseTemplateModel::class : 'implements \\' . TemplateModelInterface::class; if (false === $extendsClass) { diff --git a/tests/pest/Unit/Model/ModelFactoryWithDefaultsManagementTest.php b/tests/pest/Unit/Model/ModelFactoryWithDefaultsManagementTest.php index 15b081d..1c365cc 100644 --- a/tests/pest/Unit/Model/ModelFactoryWithDefaultsManagementTest.php +++ b/tests/pest/Unit/Model/ModelFactoryWithDefaultsManagementTest.php @@ -40,7 +40,7 @@ public function testMakeModelInitializesPropertiesWhenObjectImplementsWithDefaul ->andReturn($modelWithDefaultsMock); $modelWithDefaultsMock - ->shouldReceive('getDefaultsProvider') + ->shouldReceive('getDefaultPropertyValueProvider') ->once() ->andReturn($propertyValueProviderMock); @@ -113,7 +113,7 @@ public function testMakeModelHandlesInnerObjectsWithDefaultsInterface(): void ->andReturn($mainModelMock); $mainModelMock - ->shouldReceive('getDefaultsProvider') + ->shouldReceive('getDefaultPropertyValueProvider') ->once() ->andReturn($propertyValueProviderMock); @@ -127,7 +127,7 @@ public function testMakeModelHandlesInnerObjectsWithDefaultsInterface(): void ->with($mainModelMock, $propertyValueProviderMock); $innerModelMock1 - ->shouldReceive('getDefaultsProvider') + ->shouldReceive('getDefaultPropertyValueProvider') ->once() ->andReturn($propertyValueProviderMock); @@ -141,7 +141,7 @@ public function testMakeModelHandlesInnerObjectsWithDefaultsInterface(): void ->with($innerModelMock1, $propertyValueProviderMock); $innerModelMock2 - ->shouldReceive('getDefaultsProvider') + ->shouldReceive('getDefaultPropertyValueProvider') ->once() ->andReturn($propertyValueProviderMock); @@ -185,7 +185,7 @@ public function testMakeModelSkipsInnerObjectsWithoutDefaultsInterface(): void ->andReturn($mainModelMock); $mainModelMock - ->shouldReceive('getDefaultsProvider') + ->shouldReceive('getDefaultPropertyValueProvider') ->once() ->andReturn($propertyValueProviderMock); diff --git a/tests/pest/Unit/Object/PropertyValueProviderForModelsTest.php b/tests/pest/Unit/Object/PropertyValueProviderForModelsTest.php index daa3644..182d024 100644 --- a/tests/pest/Unit/Object/PropertyValueProviderForModelsTest.php +++ b/tests/pest/Unit/Object/PropertyValueProviderForModelsTest.php @@ -9,7 +9,7 @@ use Prosopo\Views\Interfaces\Model\ModelFactoryInterface; use Prosopo\Views\Interfaces\Object\PropertyValueProviderInterface; use Prosopo\Views\PrivateClasses\Object\PropertyValueProviderForModels; -use Prosopo\Views\TemplateModel; +use Prosopo\Views\BaseTemplateModel; use ReflectionProperty; class PropertyValueProviderForModelsTest extends TestCase @@ -51,7 +51,7 @@ public function testSupportsPropertyReturnsTrueForValidModelClass(): void // then $propertyMock->shouldReceive('getType->getName') ->once() - ->andReturn(TemplateModel::class); + ->andReturn(BaseTemplateModel::class); $propertyValueProviderMock->shouldReceive('supportsProperty') ->once() @@ -126,7 +126,7 @@ public function testGetPropertyValueCreatesModelForValidClass(): void $modelFactoryMock = Mockery::mock(ModelFactoryInterface::class); $provider = new PropertyValueProviderForModels($propertyValueProviderMock, $modelFactoryMock); $propertyMock = Mockery::mock(ReflectionProperty::class); - $model = Mockery::mock(TemplateModel::class); + $model = Mockery::mock(BaseTemplateModel::class); // when $result = fn() => $provider->getPropertyValue($propertyMock); @@ -134,7 +134,7 @@ public function testGetPropertyValueCreatesModelForValidClass(): void // then $propertyMock->shouldReceive('getType->getName') ->once() - ->andReturn(TemplateModel::class); + ->andReturn(BaseTemplateModel::class); $propertyValueProviderMock->shouldReceive('supportsProperty') ->once() @@ -143,7 +143,7 @@ public function testGetPropertyValueCreatesModelForValidClass(): void $modelFactoryMock->shouldReceive('createModel') ->once() - ->with(TemplateModel::class) + ->with(BaseTemplateModel::class) ->andReturn($model); $this->assertSame($model, $result()); diff --git a/tests/pest/Unit/ViewsTest.php b/tests/pest/Unit/ViewsManagerTest.php similarity index 86% rename from tests/pest/Unit/ViewsTest.php rename to tests/pest/Unit/ViewsManagerTest.php index c37f516..2dc0766 100644 --- a/tests/pest/Unit/ViewsTest.php +++ b/tests/pest/Unit/ViewsManagerTest.php @@ -6,14 +6,14 @@ use Exception; use PHPUnit\Framework\TestCase; -use Prosopo\Views\Views; +use Prosopo\Views\ViewsManager; -class ViewsTest extends TestCase +class ViewsManagerTest extends TestCase { public function testMakeModelThrowsExceptionForMissingClass(): void { // given - $views = new Views(); + $views = new ViewsManager(); // when $makeModel = fn()=> $views->createModel('MissingClass'); @@ -28,7 +28,7 @@ public function testMakeModelThrowsExceptionForMissingClass(): void public function testMakeModelThrowsExceptionForClassThatNotImplementTemplateModelInterface(): void { // given - $views = new Views(); + $views = new ViewsManager(); // when $makeModel = fn()=> $views->createModel(get_class($this)); @@ -43,7 +43,7 @@ public function testMakeModelThrowsExceptionForClassThatNotImplementTemplateMode public function testRenderThrowsExceptionForMissingClass(): void { // given - $views = new Views(); + $views = new ViewsManager(); // when $makeModel = fn()=> $views->renderModel('MissingClass'); @@ -58,7 +58,7 @@ public function testRenderThrowsExceptionForMissingClass(): void public function testRenderThrowsExceptionForClassThatNotImplementTemplateModelInterface(): void { // given - $views = new Views(); + $views = new ViewsManager(); // when $makeModel = fn()=> $views->renderModel(get_class($this)); @@ -73,7 +73,7 @@ public function testRenderThrowsExceptionForClassThatNotImplementTemplateModelIn public function testRenderThrowsExceptionForInstanceThatNotImplementTemplateModelInterface(): void { // given - $views = new Views(); + $views = new ViewsManager(); // when $makeModel = fn()=> $views->renderModel($this); diff --git a/tmp.php b/tmp.php new file mode 100644 index 0000000..97eae68 --- /dev/null +++ b/tmp.php @@ -0,0 +1,32 @@ +getModules() + ->setTemplateCompiler(new CompilerStubForPlainPhpSupport()); + +$viewTemplateRenderer = new ViewTemplateRenderer($viewTemplateRendererConfig); + +$views = new ViewsManager(); + +$viewNamespaceConfig = new ViewNamespaceConfig($viewTemplateRenderer); +$viewNamespaceConfig + ->setTemplatesRootPath(__DIR__ . './templates') + ->setTemplateFileExtension('.php'); + +$views->registerNamespace('MyApp\Models', $viewNamespaceConfig); From 0aef72c58a58867c8ec26d5fff958f3ce9c9f4de Mon Sep 17 00:00:00 2001 From: Maxim Akimov Date: Wed, 18 Dec 2024 10:42:22 +0200 Subject: [PATCH 10/13] publish preparation: tests --- tests/pest/Feature/ViewsManagerTest.php | 4 +- tests/pest/Unit/Model/ModelRendererTest.php | 44 ++-------------- .../ModelRendererWithEventDetailsTest.php | 50 ++++--------------- .../Unit/Template/TemplateRendererTest.php | 28 ----------- .../TemplateRendererWithCustomEscapeTest.php | 9 ++-- .../TemplateRendererWithEventDetailsTest.php | 38 +------------- .../TemplateRendererWithFileTemplateTest.php | 29 +---------- .../TemplateRendererWithModelRenderTest.php | 4 +- tmp.php | 32 ------------ 9 files changed, 23 insertions(+), 215 deletions(-) delete mode 100644 tmp.php diff --git a/tests/pest/Feature/ViewsManagerTest.php b/tests/pest/Feature/ViewsManagerTest.php index 422e44c..58b5d29 100644 --- a/tests/pest/Feature/ViewsManagerTest.php +++ b/tests/pest/Feature/ViewsManagerTest.php @@ -235,8 +235,8 @@ public function testRenderCallsTemplateErrorHandler(): void // then $this->assertSame('', $rendered); - $this->assertSame(['viewClass','template','arguments','code','error',], array_keys($receivedEventDetails)); - $this->assertSame($modelClass, $receivedEventDetails['viewClass']); + $this->assertSame(['modelClass','template','arguments','code','error',], array_keys($receivedEventDetails)); + $this->assertSame($modelClass, $receivedEventDetails['modelClass']); $this->assertSame(vfsStream::url('templates/first-model.blade.php'), $receivedEventDetails['template']); $this->assertSame('some data', $receivedEventDetails['arguments']['message']); $this->assertSame('wrong template', $receivedEventDetails['code']); diff --git a/tests/pest/Unit/Model/ModelRendererTest.php b/tests/pest/Unit/Model/ModelRendererTest.php index 04ebf8a..b85b3b8 100644 --- a/tests/pest/Unit/Model/ModelRendererTest.php +++ b/tests/pest/Unit/Model/ModelRendererTest.php @@ -8,7 +8,6 @@ use PHPUnit\Framework\TestCase; use Prosopo\Views\Interfaces\Model\ModelFactoryInterface; use Prosopo\Views\Interfaces\Model\TemplateModelInterface; -use Prosopo\Views\Interfaces\Object\ObjectReaderInterface; use Prosopo\Views\Interfaces\Template\ModelTemplateResolverInterface; use Prosopo\Views\Interfaces\Template\TemplateRendererInterface; use Prosopo\Views\PrivateClasses\Model\ModelRenderer; @@ -43,7 +42,7 @@ public function testRenderModelWithModelInstance(): void $templateRendererMock->shouldReceive('renderTemplate') ->once() - ->with('
{{ $key }}
', ['key' => 'value'], false) + ->with('
{{ $key }}
', ['key' => 'value']) ->andReturn('
value
'); // then @@ -86,7 +85,7 @@ public function testRenderModelWithModelClass(): void $templateRendererMock->shouldReceive('renderTemplate') ->once() - ->with('
{{ $key }}
', ['key' => 'value'], false) + ->with('
{{ $key }}
', ['key' => 'value']) ->andReturn('
value
'); $this->assertSame('
value
', $result()); @@ -131,7 +130,7 @@ public function testRenderModelAppliesSetupCallback(): void $templateRendererMock->shouldReceive('renderTemplate') ->once() - ->with('
{{ $key }}
', ['key' => 'modified_value'], false) + ->with('
{{ $key }}
', ['key' => 'modified_value']) ->andReturn('
modified_value
'); $this->assertSame('
modified_value
', $result()); @@ -139,41 +138,4 @@ public function testRenderModelAppliesSetupCallback(): void // apply Mockery::close(); } - - public function testRenderModelPrintsOutput(): void - { - // given - $templateRendererMock = Mockery::mock(TemplateRendererInterface::class); - $modelFactoryMock = Mockery::mock(ModelFactoryInterface::class); - $templateProviderMock = Mockery::mock(ModelTemplateResolverInterface::class); - $modelMock = Mockery::mock(TemplateModelInterface::class); - $renderer = new ModelRenderer( - $templateRendererMock, - $modelFactoryMock, - $templateProviderMock - ); - - // when - $renderModel = fn()=>$renderer->renderModel($modelMock, null, true); - - // then - $modelMock->shouldReceive('getTemplateArguments') - ->once() - ->andReturn(['key' => 'value']); - - $templateProviderMock->shouldReceive('resolveModelTemplate') - ->once() - ->with($modelMock) - ->andReturn('
{{ $key }}
'); - - $templateRendererMock->shouldReceive('renderTemplate') - ->once() - ->with('
{{ $key }}
', ['key' => 'value'], true) - ->andReturn('
value
'); - - $this->assertSame('
value
', $renderModel()); - - // apply - Mockery::close(); - } } diff --git a/tests/pest/Unit/Model/ModelRendererWithEventDetailsTest.php b/tests/pest/Unit/Model/ModelRendererWithEventDetailsTest.php index 92d1488..d54061e 100644 --- a/tests/pest/Unit/Model/ModelRendererWithEventDetailsTest.php +++ b/tests/pest/Unit/Model/ModelRendererWithEventDetailsTest.php @@ -27,16 +27,16 @@ public function testRenderModelAttachesAndDetachesEventDetails(): void // then $eventDispatcherMock->shouldReceive('registerEventDetails') ->once() - ->with('render_event', ['viewClass' => get_class($modelMock)]); + ->with('render_event', ['modelClass' => get_class($modelMock)]); $viewRendererMock->shouldReceive('renderModel') ->once() - ->with($modelMock, null, false) + ->with($modelMock, null) ->andReturn('
Rendered View
'); $eventDispatcherMock->shouldReceive('unregisterEventDetails') ->once() - ->with('render_event', ['viewClass' => get_class($modelMock)]); + ->with('render_event', ['modelClass' => get_class($modelMock)]); $this->assertSame('
Rendered View
', $result()); @@ -57,16 +57,16 @@ public function testRenderModelHandlesModelClass(): void // then $eventDispatcherMock->shouldReceive('registerEventDetails') ->once() - ->with('render_event', ['viewClass' => 'TestModelClass']); + ->with('render_event', ['modelClass' => 'TestModelClass']); $viewRendererMock->shouldReceive('renderModel') ->once() - ->with('TestModelClass', null, false) + ->with('TestModelClass', null) ->andReturn('
Rendered Model
'); $eventDispatcherMock->shouldReceive('unregisterEventDetails') ->once() - ->with('render_event', ['viewClass' => 'TestModelClass']); + ->with('render_event', ['modelClass' => 'TestModelClass']); $this->assertSame('
Rendered Model
', $result()); @@ -94,52 +94,20 @@ public function testRenderModelPassesSetupCallback(): void // then $eventDispatcherMock->shouldReceive('registerEventDetails') ->once() - ->with('render_event', ['viewClass' => get_class($modelMock)]); + ->with('render_event', ['modelClass' => get_class($modelMock)]); $viewRendererMock->shouldReceive('renderModel') ->once() - ->with($modelMock, $setupCallback, false) + ->with($modelMock, $setupCallback) ->andReturn('
Modified View
'); $eventDispatcherMock->shouldReceive('unregisterEventDetails') ->once() - ->with('render_event', ['viewClass' => get_class($modelMock)]); + ->with('render_event', ['modelClass' => get_class($modelMock)]); $this->assertSame('
Modified View
', $result()); // apply Mockery::close(); } - - public function testRenderModelHandlesDoPrintFlag(): void - { - // given - $viewRendererMock = Mockery::mock(ModelRendererInterface::class); - $eventDispatcherMock = Mockery::mock(EventDispatcherInterface::class); - $modelMock = new class { - }; - $renderer = new ModelRendererWithEventDetails($viewRendererMock, $eventDispatcherMock, 'render_event'); - - // when - $renderModel = fn()=>$renderer->renderModel($modelMock, null, true); - - // then - $eventDispatcherMock->shouldReceive('registerEventDetails') - ->once() - ->with('render_event', ['viewClass' => get_class($modelMock)]); - - $viewRendererMock->shouldReceive('renderModel') - ->once() - ->with($modelMock, null, true) - ->andReturn('
Printed View
'); - - $eventDispatcherMock->shouldReceive('unregisterEventDetails') - ->once() - ->with('render_event', ['viewClass' => get_class($modelMock)]); - - $this->assertSame('
Printed View
', $renderModel()); - - // apply - Mockery::close(); - } } diff --git a/tests/pest/Unit/Template/TemplateRendererTest.php b/tests/pest/Unit/Template/TemplateRendererTest.php index fa42bea..0f33e95 100644 --- a/tests/pest/Unit/Template/TemplateRendererTest.php +++ b/tests/pest/Unit/Template/TemplateRendererTest.php @@ -59,34 +59,6 @@ public function testRenderTemplateDoesNotPrintByDefault(): void Mockery::close(); } - public function testRenderTemplatePrintsWhenDoPrintIsTrue(): void - { - // given - $templateExecutor = Mockery::mock(CodeRunnerInterface::class); - $renderer = new TemplateRenderer($templateExecutor); - - // when - $result = fn() => $renderer->renderTemplate('

{{ $title }}

', ['title' => 'Welcome'], true); - - // then - $templateExecutor->shouldReceive('runCode') - ->once() - ->with('

{{ $title }}

', ['title' => 'Welcome']) - ->andReturnUsing(function () { - echo '

Welcome

'; - }); - - ob_start(); - $rendered = $result(); - $output = ob_get_clean(); - - $this->assertSame('

Welcome

', $rendered); - $this->assertSame('

Welcome

', $output); - - // apply - Mockery::close(); - } - public function testRenderTemplateHandlesEmptyTemplate(): void { // given diff --git a/tests/pest/Unit/Template/TemplateRendererWithCustomEscapeTest.php b/tests/pest/Unit/Template/TemplateRendererWithCustomEscapeTest.php index 39e82de..14cb652 100644 --- a/tests/pest/Unit/Template/TemplateRendererWithCustomEscapeTest.php +++ b/tests/pest/Unit/Template/TemplateRendererWithCustomEscapeTest.php @@ -33,8 +33,7 @@ public function testRenderTemplateUsesCustomEscapeCallback(): void Mockery::on(function ($variables) { return is_callable($variables['escape']) && call_user_func($variables['escape'], 'test') === 'TEST'; - }), - false + }) ) ->andReturn('
TEST
'); @@ -68,8 +67,7 @@ public function testRenderTemplateUsesDefaultEscapeCallback(): void Mockery::on(function ($variables) { return is_callable($variables['escape']) && call_user_func($variables['escape'], '') === '<script>alert("test")</script>'; - }), - false + }) ) ->andReturn('
<script>alert("test")</script>
'); @@ -101,8 +99,7 @@ public function testRenderTemplateMergesVariablesWithEscapeCallback(): void return isset($mergedVariables['escape']) && is_callable($mergedVariables['escape']) && $mergedVariables['escape']('hello') === 'hello'; - }), - false + }) ) ->andReturn('
hello
'); diff --git a/tests/pest/Unit/Template/TemplateRendererWithEventDetailsTest.php b/tests/pest/Unit/Template/TemplateRendererWithEventDetailsTest.php index 353127e..6276382 100644 --- a/tests/pest/Unit/Template/TemplateRendererWithEventDetailsTest.php +++ b/tests/pest/Unit/Template/TemplateRendererWithEventDetailsTest.php @@ -29,7 +29,7 @@ public function testRenderTemplateAttachesAndDetachesEventDetails(): void $templateRendererMock->shouldReceive('renderTemplate') ->once() - ->with('
{{ $var }}
', ['var' => 'Test Content'], false) + ->with('
{{ $var }}
', ['var' => 'Test Content']) ->andReturn('
Test Content
'); $eventDispatcherMock->shouldReceive('unregisterEventDetails') @@ -42,40 +42,6 @@ public function testRenderTemplateAttachesAndDetachesEventDetails(): void Mockery::close(); } - public function testRenderTemplatePassesDoPrintFlag(): void - { - // given - $templateRendererMock = Mockery::mock(TemplateRendererInterface::class); - $eventDispatcherMock = Mockery::mock(EventDispatcherInterface::class); - $renderer = new TemplateRendererWithEventDetails($templateRendererMock, $eventDispatcherMock, 'render_event'); - - // when - $result = fn() => $renderer->renderTemplate( - '

{{ $message }}

', - ['message' => 'Hello, World!'], - true - ); - - // then - $eventDispatcherMock->shouldReceive('registerEventDetails') - ->once() - ->with('render_event', ['template' => '

{{ $message }}

']); - - $templateRendererMock->shouldReceive('renderTemplate') - ->once() - ->with('

{{ $message }}

', ['message' => 'Hello, World!'], true) - ->andReturn('

Hello, World!

'); - - $eventDispatcherMock->shouldReceive('unregisterEventDetails') - ->once() - ->with('render_event', ['template' => '

{{ $message }}

']); - - $this->assertSame('

Hello, World!

', $result()); - - // apply - Mockery::close(); - } - public function testRenderTemplateHandlesEmptyTemplate(): void { // given @@ -93,7 +59,7 @@ public function testRenderTemplateHandlesEmptyTemplate(): void $templateRendererMock->shouldReceive('renderTemplate') ->once() - ->with('', [], false) + ->with('', []) ->andReturn(''); $eventDispatcherMock->shouldReceive('unregisterEventDetails') diff --git a/tests/pest/Unit/Template/TemplateRendererWithFileTemplateTest.php b/tests/pest/Unit/Template/TemplateRendererWithFileTemplateTest.php index 4b41d1d..5c244bc 100644 --- a/tests/pest/Unit/Template/TemplateRendererWithFileTemplateTest.php +++ b/tests/pest/Unit/Template/TemplateRendererWithFileTemplateTest.php @@ -28,7 +28,7 @@ public function testRenderTemplateReadsFileAndRendersContent(): void // then $templateRendererMock->shouldReceive('renderTemplate') ->once() - ->with('
{{ $key }}
', ['key' => 'value'], false) + ->with('
{{ $key }}
', ['key' => 'value']) ->andReturn('
value
'); $this->assertSame('
value
', $result()); @@ -51,7 +51,7 @@ public function testRenderTemplateReturnsEmptyStringForMissingFile(): void // then $templateRendererMock->shouldReceive('renderTemplate') ->once() - ->with('', ['key' => 'value'], false) + ->with('', ['key' => 'value']) ->andReturn(''); $this->assertSame('', $result()); @@ -59,29 +59,4 @@ public function testRenderTemplateReturnsEmptyStringForMissingFile(): void // apply Mockery::close(); } - - public function testRenderTemplateHandlesDoPrintFlag(): void - { - // given - vfsStream::setup('templates', null, [ - 'template.blade.php' => '
{{ $key }}
', - ]); - $templateFilePath = vfsStream::url('templates/template.blade.php'); - $templateRendererMock = Mockery::mock(TemplateRendererInterface::class); - $renderer = new TemplateRendererWithFileTemplate($templateRendererMock); - - // when - $result = fn() => $renderer->renderTemplate($templateFilePath, ['key' => 'value'], true); - - // then - $templateRendererMock->shouldReceive('renderTemplate') - ->once() - ->with('
{{ $key }}
', ['key' => 'value'], true) - ->andReturn('
value
'); - - $this->assertSame('
value
', $result()); - - // apply - Mockery::close(); - } } diff --git a/tests/pest/Unit/Template/TemplateRendererWithModelRenderTest.php b/tests/pest/Unit/Template/TemplateRendererWithModelRenderTest.php index 5757e59..26cfec4 100644 --- a/tests/pest/Unit/Template/TemplateRendererWithModelRenderTest.php +++ b/tests/pest/Unit/Template/TemplateRendererWithModelRenderTest.php @@ -40,7 +40,7 @@ public function testRenderTemplateRendersInnerModels(): void 'key1' => 'value1', 'key2' => '
Rendered Model
', 'key3' => ['nestedKey' => '
Rendered Model
'], - ], false) + ]) ->andReturn('
Final Rendered Output
'); $this->assertEquals('
Final Rendered Output
', $result()); @@ -80,7 +80,7 @@ public function testRenderTemplateIgnoresNonModelObjects(): void 'key2' => 123, 'key3' => ['nestedKey' => 'string'], 'key4' => $nonModelObject, - ], false) + ]) ->andReturn('
Final Rendered Output
'); $this->assertEquals('
Final Rendered Output
', $result()); diff --git a/tmp.php b/tmp.php deleted file mode 100644 index 97eae68..0000000 --- a/tmp.php +++ /dev/null @@ -1,32 +0,0 @@ -getModules() - ->setTemplateCompiler(new CompilerStubForPlainPhpSupport()); - -$viewTemplateRenderer = new ViewTemplateRenderer($viewTemplateRendererConfig); - -$views = new ViewsManager(); - -$viewNamespaceConfig = new ViewNamespaceConfig($viewTemplateRenderer); -$viewNamespaceConfig - ->setTemplatesRootPath(__DIR__ . './templates') - ->setTemplateFileExtension('.php'); - -$views->registerNamespace('MyApp\Models', $viewNamespaceConfig); From b96f0e46bb3b96b8bafdfeea676c726426485cac Mon Sep 17 00:00:00 2001 From: Maxim Akimov Date: Wed, 18 Dec 2024 11:38:12 +0200 Subject: [PATCH 11/13] publish preparation: readme --- README.md | 43 +++- .../CodeRunner/CodeRunnerWithErrorHandler.php | 4 +- private-classes/Model/ModelFactory.php | 17 +- .../Object/PropertyValueProviderForModels.php | 2 +- .../Template/FileModelTemplateResolver.php | 10 +- private-classes/View/ViewNamespace.php | 30 ++- src/BaseTemplateModel.php | 18 +- src/View/ViewNamespaceConfig.php | 26 ++- src/View/ViewTemplateRenderer.php | 2 +- src/View/ViewTemplateRendererConfig.php | 12 +- .../Feature/BladeTemplateRendererTest.php | 6 +- tests/pest/Feature/ViewsManagerTest.php | 189 +++++++++++++----- tests/pest/Unit/Model/ModelFactoryTest.php | 52 ++++- 13 files changed, 315 insertions(+), 96 deletions(-) diff --git a/README.md b/README.md index fee0505..a52a33e 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ custom [Blade](https://laravel.com/docs/11.x/blade) implementation as a default ### Benefits -* **Blazing fast:** Outperforms the original Laravel Blade (see the [Benchmark chapter](#4-benchmark)). +* **Blazing fast:** Outperforms the original Laravel Blade (see the [Benchmark section](#4-benchmark)). * **Zero Dependencies:** Lightweight and easy to integrate into any project. * **Wide Compatibility:** PHP 7.4+, 8.0+ * **Adherence to the [SOLID principles](https://en.wikipedia.org/wiki/SOLID):** The architecture allows you to easily @@ -68,6 +68,7 @@ Model template (Blade is used in this example):

Your month income is {{ $total() }}, from which {{ $salary }} is a salary, and {{ $bonus }} is a bonus. +Est. taxes: {{ $innerModel->taxes($salary) }}

{!! $innerModel !!} @@ -87,9 +88,18 @@ from which {{ $salary }} is a salary, and {{ $bonus }} is a bonus. maintain flexibility and avoid specifying the exact component. -The `BaseTemplateModel` class implements the `TemplateModelInterface`. During rendering, any inner objects that also -implement -`TemplateModelInterface` will be automatically rendered and passed into the template as strings. +By default, inner models are passed to templates as objects, enabling you to call their public methods directly. For +example: + +`{{ $$innerModel->calc($localVar) }}`. + +The `BaseTemplateModel` class overrides the `__toString()` method, allowing inner models to be rendered as strings using +echo statements, which supports HTML output. For instance: + +`{!! $innerModel !!}` + +If you prefer, you can configure the `ViewsManager` to pass models as strings instead of objects. Refer to the ` +ViewsManager` section for details. ### 1.3) Custom property defaults @@ -144,6 +154,17 @@ class AnyClass implements TemplateModelInterface } ``` +Note: If you plan to define inner models, remember that the default `__toString()` implementation is not provided. You +will need to either implement it yourself or enable the option to pass models as strings in the `ViewsManager` +configuration: + +```php +$namespaceConfig->setModelsAsStringsInTemplates(true); +``` + +When this option is enabled, the renderer will automatically convert objects implementing `TemplateModelInterface` into +strings before passing them to the template. + ## 2. Views Manager The `ViewsManager` class provides the `registerNamespace`, `createModel` and `renderModel` methods. It acts as a @@ -174,7 +195,9 @@ $namespaceConfig = (new ViewNamespaceConfig($viewTemplateRenderer)) // optional setting: ->setTemplateErrorHandler(function (array $eventDetails) { // logging, notifying, whatever. - }); + }) + // this option enables inner models rendering before passing them into the template + ->setModelsAsStringsInTemplates(true); // (This line is necessary only if you defined the templateErrorHandler) $namespaceConfig->getModules() @@ -234,7 +257,8 @@ Advice: The `ViewsManager` class implements three interfaces: `ViewNamespaceMana When passing the `ViewsManager` instance to your methods, use one of these interfaces as the argument type instead of the `ViewsManager` class itself. -This approach ensures that only the specific actions you expect are accessible, promoting cleaner and more maintainable code. +This approach ensures that only the specific actions you expect are accessible, promoting cleaner and more maintainable +code. ### 2.4) Automated templates matching @@ -324,7 +348,8 @@ $namespaceConfig->getModules() ### 2.6) Namespace mixing -> Fun Fact: The `ViewsManager` class not only supporting multiple namespaces, but also enabling you to use Models from one +> Fun Fact: The `ViewsManager` class not only supporting multiple namespaces, but also enabling you to use Models from +> one > namespace within another, preserving their individual setup. Example of multi-namespace usage: @@ -399,7 +424,7 @@ use Prosopo\Views\View\ViewTemplateRenderer; use Prosopo\Views\View\ViewTemplateRendererConfig; $viewRendererConfig = new ViewTemplateRendererConfig(); -$viewRendererConfig->setIsFileBasedTemplate(false); +$viewRendererConfig->setFileBasedTemplates(false); $viewTemplateRenderer = new ViewTemplateRenderer($viewRendererConfig); @@ -425,7 +450,7 @@ use Prosopo\Views\View\ViewTemplateRendererConfig; $viewRendererConfig = (new ViewTemplateRendererConfig()) // By default, the Renderer expect a file name. // Set to false if to work with strings - ->setIsFileBasedTemplate(true) + ->setFileBasedTemplates(true) ->setTemplateErrorHandler(function (array $eventDetails): void { // Can be used for logging, notifying, etc. }) diff --git a/private-classes/CodeRunner/CodeRunnerWithErrorHandler.php b/private-classes/CodeRunner/CodeRunnerWithErrorHandler.php index 5c4368a..b07ab06 100644 --- a/private-classes/CodeRunner/CodeRunnerWithErrorHandler.php +++ b/private-classes/CodeRunner/CodeRunnerWithErrorHandler.php @@ -20,9 +20,9 @@ final class CodeRunnerWithErrorHandler implements CodeRunnerInterface private string $errorEventName; public function __construct( - CodeRunnerInterface $codeExecutor, + CodeRunnerInterface $codeExecutor, EventDispatcherInterface $eventDispatcher, - string $errorEventName + string $errorEventName ) { $this->codeExecutor = $codeExecutor; $this->eventDispatcher = $eventDispatcher; diff --git a/private-classes/Model/ModelFactory.php b/private-classes/Model/ModelFactory.php index 3802e0c..df302d7 100644 --- a/private-classes/Model/ModelFactory.php +++ b/private-classes/Model/ModelFactory.php @@ -7,6 +7,8 @@ use Prosopo\Views\Interfaces\Model\ModelFactoryInterface; use Prosopo\Views\Interfaces\Object\ObjectReaderInterface; use Prosopo\Views\Interfaces\Object\PropertyValueProviderInterface; +use Prosopo\Views\Interfaces\Template\ModelTemplateResolverInterface; +use Prosopo\Views\Interfaces\Template\TemplateRendererInterface; /** * This class is marked as a final and placed under the 'Private' namespace to prevent anyone from using it directly. @@ -16,17 +18,28 @@ final class ModelFactory implements ModelFactoryInterface { private ObjectReaderInterface $objectReader; private PropertyValueProviderInterface $propertyValueProvider; + private ModelTemplateResolverInterface $modelTemplateResolver; + private TemplateRendererInterface $templateRenderer; public function __construct( ObjectReaderInterface $objectReader, - PropertyValueProviderInterface $propertyValueProvider + PropertyValueProviderInterface $propertyValueProvider, + ModelTemplateResolverInterface $modelTemplateResolver, + TemplateRendererInterface $templateRenderer ) { $this->objectReader = $objectReader; $this->propertyValueProvider = $propertyValueProvider; + $this->modelTemplateResolver = $modelTemplateResolver; + $this->templateRenderer = $templateRenderer; } public function createModel(string $modelClass) { - return new $modelClass($this->objectReader, $this->propertyValueProvider); + return new $modelClass( + $this->objectReader, + $this->propertyValueProvider, + $this->modelTemplateResolver, + $this->templateRenderer + ); } } diff --git a/private-classes/Object/PropertyValueProviderForModels.php b/private-classes/Object/PropertyValueProviderForModels.php index 2758bb9..c5d4e6d 100644 --- a/private-classes/Object/PropertyValueProviderForModels.php +++ b/private-classes/Object/PropertyValueProviderForModels.php @@ -4,9 +4,9 @@ namespace Prosopo\Views\PrivateClasses\Object; +use Prosopo\Views\BaseTemplateModel; use Prosopo\Views\Interfaces\Model\ModelFactoryInterface; use Prosopo\Views\Interfaces\Object\PropertyValueProviderInterface; -use Prosopo\Views\BaseTemplateModel; use ReflectionProperty; /** diff --git a/private-classes/Template/FileModelTemplateResolver.php b/private-classes/Template/FileModelTemplateResolver.php index c861fba..f0bc621 100644 --- a/private-classes/Template/FileModelTemplateResolver.php +++ b/private-classes/Template/FileModelTemplateResolver.php @@ -23,12 +23,12 @@ final class FileModelTemplateResolver implements ModelTemplateResolverInterface private ModelNamespaceResolverInterface $modelNamespaceProvider; public function __construct( - string $namespace, - string $templatesRootPath, - string $extension, - bool $isFileBasedTemplate, + string $namespace, + string $templatesRootPath, + string $extension, + bool $isFileBasedTemplate, ModelNamespaceResolverInterface $modelNamespaceProvider, - ModelNameResolverInterface $modelNameProvider + ModelNameResolverInterface $modelNameProvider ) { $this->templatesRootPath = $templatesRootPath; $this->namespace = $namespace; diff --git a/private-classes/View/ViewNamespace.php b/private-classes/View/ViewNamespace.php index 2e6e08c..5663e4b 100644 --- a/private-classes/View/ViewNamespace.php +++ b/private-classes/View/ViewNamespace.php @@ -81,17 +81,17 @@ public function __construct( new ModelNameResolver(new ObjectClassReader()) : $modelNameProvider; - $templateProvider = $modules->getModelTemplateResolver(); - $templateProvider = null === $templateProvider ? + $modelTemplateResolver = $modules->getModelTemplateResolver(); + $modelTemplateResolver = null === $modelTemplateResolver ? new FileModelTemplateResolver( $namespace, $config->getTemplatesRootPath(), $config->getTemplateFileExtension(), - $config->isFileBasedTemplate(), + $config->fileBasedTemplates(), $modelNamespaceProvider, $modelNameProvider ) : - $templateProvider; + $modelTemplateResolver; $propertyValueProvider = $modules->getPropertyValueProvider(); $propertyValueProvider = null === $propertyValueProvider ? @@ -112,13 +112,25 @@ public function __construct( // Without null check - templateRenderer is a mandatory module. $templateRenderer = $modules->getTemplateRenderer(); - $templateRenderer = new TemplateRendererWithModelsRender($templateRenderer, $modelRendererWithNamespace); + $templateRendererWithModelsRender = new TemplateRendererWithModelsRender( + $templateRenderer, + $modelRendererWithNamespace + ); + + if (true === $config->modelsAsStringsInTemplates()) { + $templateRenderer = $templateRendererWithModelsRender; + } - //// 2. Real Factory and Renderer creation (used in the Views class): + //// 2. Real Factory and Renderer creation (used in the ViewsManager class): $realModelFactory = $modules->getModelFactory(); $realModelFactory = null === $realModelFactory ? - new ModelFactory($objectReader, $propertyValueProvider) : + new ModelFactory( + $objectReader, + $propertyValueProvider, + $modelTemplateResolver, + $templateRendererWithModelsRender + ) : $realModelFactory; $realModelFactory = new ModelFactoryWithDefaultsManagement( @@ -133,7 +145,7 @@ public function __construct( new ModelRenderer( $templateRenderer, $modelFactoryWithNamespaces, - $templateProvider + $modelTemplateResolver ) : $realModelRenderer; @@ -148,7 +160,7 @@ public function __construct( $modules->setEventDispatcher($eventDispatcher) ->setObjectReader($objectReader) ->setObjectPropertyWriter($objectPropertyWriter) - ->setModelTemplateResolver($templateProvider) + ->setModelTemplateResolver($modelTemplateResolver) ->setPropertyValueProvider($propertyValueProvider) ->setModelFactory($realModelFactory) ->setModelRenderer($realModelRenderer) diff --git a/src/BaseTemplateModel.php b/src/BaseTemplateModel.php index d628ce9..f493d96 100644 --- a/src/BaseTemplateModel.php +++ b/src/BaseTemplateModel.php @@ -8,11 +8,15 @@ use Prosopo\Views\Interfaces\Model\TemplateModelWithDefaultsInterface; use Prosopo\Views\Interfaces\Object\ObjectReaderInterface; use Prosopo\Views\Interfaces\Object\PropertyValueProviderInterface; +use Prosopo\Views\Interfaces\Template\ModelTemplateResolverInterface; +use Prosopo\Views\Interfaces\Template\TemplateRendererInterface; abstract class BaseTemplateModel implements TemplateModelInterface, TemplateModelWithDefaultsInterface { private ObjectReaderInterface $objectReader; private PropertyValueProviderInterface $propertyValueProviderForDefaults; + private ModelTemplateResolverInterface $modelTemplateResolver; + private TemplateRendererInterface $templateRenderer; /** * The constructor is marked as final to prevent accidental argument overrides. @@ -37,10 +41,14 @@ abstract class BaseTemplateModel implements TemplateModelInterface, TemplateMode */ final public function __construct( ObjectReaderInterface $objectPropertyReader, - PropertyValueProviderInterface $propertyValueProviderForDefaults + PropertyValueProviderInterface $propertyValueProviderForDefaults, + ModelTemplateResolverInterface $modelTemplateResolver, + TemplateRendererInterface $templateRenderer ) { $this->objectReader = $objectPropertyReader; $this->propertyValueProviderForDefaults = $propertyValueProviderForDefaults; + $this->modelTemplateResolver = $modelTemplateResolver; + $this->templateRenderer = $templateRenderer; $this->setCustomDefaults(); } @@ -58,4 +66,12 @@ public function getDefaultPropertyValueProvider(): PropertyValueProviderInterfac protected function setCustomDefaults(): void { } + + public function __toString() + { + $template = $this->modelTemplateResolver->resolveModelTemplate($this); + $arguments = $this->getTemplateArguments(); + + return $this->templateRenderer->renderTemplate($template, $arguments); + } } diff --git a/src/View/ViewNamespaceConfig.php b/src/View/ViewNamespaceConfig.php index b4a24e0..24fb668 100644 --- a/src/View/ViewNamespaceConfig.php +++ b/src/View/ViewNamespaceConfig.php @@ -14,7 +14,8 @@ final class ViewNamespaceConfig { private string $templatesRootPath; private string $templateFileExtension; - private bool $isFileBasedTemplate; + private bool $fileBasedTemplates; + private bool $modelsAsStringsInTemplates; /** * @var callable(array $eventDetails): void|null */ @@ -31,7 +32,8 @@ public function __construct(TemplateRendererInterface $templateRenderer) { $this->templatesRootPath = ''; $this->templateFileExtension = ''; - $this->isFileBasedTemplate = true; + $this->fileBasedTemplates = true; + $this->modelsAsStringsInTemplates = false; $this->templateErrorHandler = null; $this->defaultPropertyValues = array( 'array' => array(), @@ -57,9 +59,14 @@ public function getTemplateFileExtension(): string return $this->templateFileExtension; } - public function isFileBasedTemplate(): bool + public function fileBasedTemplates(): bool { - return $this->isFileBasedTemplate; + return $this->fileBasedTemplates; + } + + public function modelsAsStringsInTemplates(): bool + { + return $this->modelsAsStringsInTemplates; } /** @@ -104,9 +111,16 @@ public function setTemplateFileExtension(string $templateFileExtension): self return $this; } - public function setIsFileBasedTemplate(bool $isFileBasedTemplate): self + public function setFileBasedTemplates(bool $fileBasedTemplates): self + { + $this->fileBasedTemplates = $fileBasedTemplates; + + return $this; + } + + public function setModelsAsStringsInTemplates(bool $modelsAsStringsInTemplates): self { - $this->isFileBasedTemplate = $isFileBasedTemplate; + $this->modelsAsStringsInTemplates = $modelsAsStringsInTemplates; return $this; } diff --git a/src/View/ViewTemplateRenderer.php b/src/View/ViewTemplateRenderer.php index e08f432..454f138 100644 --- a/src/View/ViewTemplateRenderer.php +++ b/src/View/ViewTemplateRenderer.php @@ -65,7 +65,7 @@ public function __construct(?ViewTemplateRendererConfig $config = null) new TemplateRenderer($codeExecutor) : $templateRenderer; - if (true === $config->isFileBasedTemplate()) { + if (true === $config->fileBasedTemplates()) { $templateRenderer = new TemplateRendererWithFileTemplate($templateRenderer); } diff --git a/src/View/ViewTemplateRendererConfig.php b/src/View/ViewTemplateRendererConfig.php index 2df00e7..5bef2b2 100644 --- a/src/View/ViewTemplateRendererConfig.php +++ b/src/View/ViewTemplateRendererConfig.php @@ -10,7 +10,7 @@ */ final class ViewTemplateRendererConfig { - private bool $isFileBasedTemplate; + private bool $fileBasedTemplates; private string $escapeVariableName; private string $templateErrorEventName; /** @@ -35,7 +35,7 @@ final class ViewTemplateRendererConfig public function __construct() { - $this->isFileBasedTemplate = true; + $this->fileBasedTemplates = true; $this->escapeVariableName = 'escape'; $this->templateErrorEventName = 'template_error'; $this->globalVariables = []; @@ -49,9 +49,9 @@ public function __construct() //// Getters: - public function isFileBasedTemplate(): bool + public function fileBasedTemplates(): bool { - return $this->isFileBasedTemplate; + return $this->fileBasedTemplates; } /** @@ -103,9 +103,9 @@ public function getModules(): ViewTemplateRendererModules //// Setters: - public function setIsFileBasedTemplate(bool $isFileBasedTemplate): self + public function setFileBasedTemplates(bool $fileBasedTemplates): self { - $this->isFileBasedTemplate = $isFileBasedTemplate; + $this->fileBasedTemplates = $fileBasedTemplates; return $this; } diff --git a/tests/pest/Feature/BladeTemplateRendererTest.php b/tests/pest/Feature/BladeTemplateRendererTest.php index 72df6cb..d38bb84 100644 --- a/tests/pest/Feature/BladeTemplateRendererTest.php +++ b/tests/pest/Feature/BladeTemplateRendererTest.php @@ -32,7 +32,7 @@ public function testRendersStringWhenFileBasedFlagIsDisabled(): void { // given $bladeRendererConfig = new ViewTemplateRendererConfig(); - $bladeRendererConfig->setIsFileBasedTemplate(false); + $bladeRendererConfig->setFileBasedTemplates(false); $bladeRenderer = new ViewTemplateRenderer($bladeRendererConfig); // when @@ -48,7 +48,7 @@ public function testTemplateErrorHandlerIsCalledOnError(): void { // given $bladeRendererConfig = new ViewTemplateRendererConfig(); - $bladeRendererConfig->setIsFileBasedTemplate(false); + $bladeRendererConfig->setFileBasedTemplates(false); $receivedEventDetails = null; $bladeRendererConfig->setTemplateErrorHandler(function (array $eventDetails) use (&$receivedEventDetails) { $receivedEventDetails = $eventDetails; @@ -73,7 +73,7 @@ public function testTemplateErrorHandlerNotCalledWithoutReason(): void { // given $bladeRendererConfig = new ViewTemplateRendererConfig(); - $bladeRendererConfig->setIsFileBasedTemplate(false); + $bladeRendererConfig->setFileBasedTemplates(false); $receivedEventDetails = null; $bladeRendererConfig->setTemplateErrorHandler(function (array $eventDetails) use (&$receivedEventDetails) { $receivedEventDetails = $eventDetails; diff --git a/tests/pest/Feature/ViewsManagerTest.php b/tests/pest/Feature/ViewsManagerTest.php index 58b5d29..6f78632 100644 --- a/tests/pest/Feature/ViewsManagerTest.php +++ b/tests/pest/Feature/ViewsManagerTest.php @@ -7,9 +7,9 @@ use org\bovigo\vfs\vfsStream; use ParseError; use PHPUnit\Framework\TestCase; +use Prosopo\Views\BaseTemplateModel; use Prosopo\Views\Interfaces\Model\TemplateModelInterface; use Prosopo\Views\Interfaces\Template\TemplateCompilerInterface; -use Prosopo\Views\BaseTemplateModel; use Prosopo\Views\View\ViewNamespaceConfig; use Prosopo\Views\View\ViewTemplateRenderer; use Prosopo\Views\View\ViewTemplateRendererConfig; @@ -165,12 +165,12 @@ public function testRenderModelPassesStringToTemplateRendererWhenFileBasedModeIs // given vfsStream::setup('templates', null, ['first-model.blade.php' => '{{ $message }}']); $bladeConfig = new ViewTemplateRendererConfig(); - $bladeConfig->setIsFileBasedTemplate(false); + $bladeConfig->setFileBasedTemplates(false); $bladeRenderer = new ViewTemplateRenderer($bladeConfig); $namespaceConfig = (new ViewNamespaceConfig($bladeRenderer)) ->setTemplatesRootPath(vfsStream::url('templates')) ->setTemplateFileExtension('.blade.php') - ->setIsFileBasedTemplate(false); + ->setFileBasedTemplates(false); $modelNamespace = $this->defineRealModelClass( __METHOD__, 'FirstModel', @@ -378,24 +378,72 @@ public function testRenderSupportsDifferentNamespaces(): void $this->assertSame('Looks good!', $views->renderModel($secondModelClass)); } - public function testRenderIncludesInnerModel(): void + public function testRenderSupportsCustomCompiler(): void { // given vfsStream::setup('templates', null, [ - 'inner-model.blade.php' => 'inner!', - 'top-model.blade.php' => 'Hey {!! $inner !!}', + 'pure.php' => '', ]); - $bladeRenderer = new ViewTemplateRenderer(); - $namespaceConfig = (new ViewNamespaceConfig($bladeRenderer)) + $compilerStub = new class implements TemplateCompilerInterface{ + public function compileTemplate(string $template): string + { + return $template; + } + }; + $viewTemplateRendererConfig = new ViewTemplateRendererConfig(); + $viewTemplateRendererConfig->getModules() + ->setTemplateCompiler($compilerStub); + $viewTemplateRenderer = new ViewTemplateRenderer($viewTemplateRendererConfig); + $views = new ViewsManager(); + $viewNamespaceConfig = new ViewNamespaceConfig($viewTemplateRenderer); + $modelNamespace = $this->defineRealModelClass( + __METHOD__, + 'Pure', + [ + [ + 'name' => 'message', + 'visibility' => 'public', + ] + ], + false + ); + + // when + $viewNamespaceConfig ->setTemplatesRootPath(vfsStream::url('templates')) + ->setTemplateFileExtension('.php'); + + $views->registerNamespace($modelNamespace, $viewNamespaceConfig); + + $modelClass = $modelNamespace . '\\Pure'; + $model = new $modelClass(); + $model->message = 'Hello World!'; + + // then + $this->assertSame('Hello World! and again: Hello World!', $views->renderModel($model)); + } + + public function testRenderPassesInnerModelsAsObjects(): void + { + // given + vfsStream::setup('top', null, [ + 'folder1' => ['top-model.blade.php' => 'Hey {{ true === is_object($inner)? "inner object": "string" }}'], + 'folder2' => [ 'inner-model.blade.php' => 'inner!'], + ]); + $bladeRenderer = new ViewTemplateRenderer(); + $firstNamespaceConfig = (new ViewNamespaceConfig($bladeRenderer)) + ->setTemplatesRootPath(vfsStream::url('top/folder1')) ->setTemplateFileExtension('.blade.php'); - $this->defineRealModelClass( - __METHOD__, + $secondNamespaceConfig = (new ViewNamespaceConfig($bladeRenderer)) + ->setTemplatesRootPath(vfsStream::url('top/folder2')) + ->setTemplateFileExtension('.blade.php'); + $secondNamespace = $this->defineRealModelClass( + __METHOD__ . '__second', 'InnerModel', [], false ); - $modelNamespace = $this->defineRealModelClass( + $firstNamespace = $this->defineRealModelClass( __METHOD__, 'TopModel', [ @@ -406,66 +454,69 @@ public function testRenderIncludesInnerModel(): void ], false ); - $views = new ViewsManager(); + $viewsManager = new ViewsManager(); // when - $views->registerNamespace($modelNamespace, $namespaceConfig); + $viewsManager->registerNamespace($firstNamespace, $firstNamespaceConfig); + $viewsManager->registerNamespace($secondNamespace, $secondNamespaceConfig); - $innerModelClass = $modelNamespace . '\\InnerModel'; - $topModelClass = $modelNamespace . '\\TopModel'; + $innerModelClass = $secondNamespace . '\\InnerModel'; + $topModelClass = $firstNamespace . '\\TopModel'; $topModel = new $topModelClass(); $topModel->inner = new $innerModelClass(); // then - $this->assertSame('Hey inner!', $views->renderModel($topModel)); + $this->assertSame('Hey inner object', $viewsManager->renderModel($topModel)); } - public function testRenderSupportsCustomCompiler(): void + public function testRenderSupportsInnerModelPrintWhenModelExtendsTheBaseClass(): void { // given - vfsStream::setup('templates', null, [ - 'pure.php' => '', + vfsStream::setup('top', null, [ + 'folder1' => ['top-model.blade.php' => 'Hey {{ $inner }}'], + 'folder2' => [ 'inner-model.blade.php' => 'inner!'], ]); - $compilerStub = new class implements TemplateCompilerInterface{ - public function compileTemplate(string $template): string - { - return $template; - } - }; - $viewTemplateRendererConfig = new ViewTemplateRendererConfig(); - $viewTemplateRendererConfig->getModules() - ->setTemplateCompiler($compilerStub); - $viewTemplateRenderer = new ViewTemplateRenderer($viewTemplateRendererConfig); - $views = new ViewsManager(); - $viewNamespaceConfig = new ViewNamespaceConfig($viewTemplateRenderer); - $modelNamespace = $this->defineRealModelClass( + $bladeRenderer = new ViewTemplateRenderer(); + $firstNamespaceConfig = (new ViewNamespaceConfig($bladeRenderer)) + ->setTemplatesRootPath(vfsStream::url('top/folder1')) + ->setTemplateFileExtension('.blade.php'); + $secondNamespaceConfig = (new ViewNamespaceConfig($bladeRenderer)) + ->setTemplatesRootPath(vfsStream::url('top/folder2')) + ->setTemplateFileExtension('.blade.php'); + $secondNamespace = $this->defineRealModelClass( + __METHOD__ . '__second', + 'InnerModel', + [], + true + ); + $innerModelClass = $secondNamespace . '\\InnerModel'; + $firstNamespace = $this->defineRealModelClass( __METHOD__, - 'Pure', + 'TopModel', [ [ - 'name' => 'message', + 'name' => 'inner', + 'type' => '\\' . $innerModelClass, 'visibility' => 'public', ] ], - false + true ); + $viewsManager = new ViewsManager(); // when - $viewNamespaceConfig - ->setTemplatesRootPath(vfsStream::url('templates')) - ->setTemplateFileExtension('.php'); + $viewsManager->registerNamespace($firstNamespace, $firstNamespaceConfig); + $viewsManager->registerNamespace($secondNamespace, $secondNamespaceConfig); - $views->registerNamespace($modelNamespace, $viewNamespaceConfig); - - $modelClass = $modelNamespace . '\\Pure'; - $model = new $modelClass(); - $model->message = 'Hello World!'; + $topModelClass = $firstNamespace . '\\TopModel'; + $topModel = $viewsManager->createModel($topModelClass); + $topModel->inner = $viewsManager->createModel($innerModelClass); // then - $this->assertSame('Hello World! and again: Hello World!', $views->renderModel($model)); + $this->assertSame('Hey inner!', $viewsManager->renderModel($topModel)); } - public function testRenderIncludesInnerModelFromDifferentNamespace(): void + public function testRenderPassesInnerModelsAsStringsWhenFlagIsSet(): void { // given vfsStream::setup('top', null, [ @@ -475,7 +526,55 @@ public function testRenderIncludesInnerModelFromDifferentNamespace(): void $bladeRenderer = new ViewTemplateRenderer(); $firstNamespaceConfig = (new ViewNamespaceConfig($bladeRenderer)) ->setTemplatesRootPath(vfsStream::url('top/folder1')) + ->setTemplateFileExtension('.blade.php') + ->setModelsAsStringsInTemplates(true); + $secondNamespaceConfig = (new ViewNamespaceConfig($bladeRenderer)) + ->setTemplatesRootPath(vfsStream::url('top/folder2')) ->setTemplateFileExtension('.blade.php'); + $secondNamespace = $this->defineRealModelClass( + __METHOD__ . '__second', + 'InnerModel', + [], + false + ); + $firstNamespace = $this->defineRealModelClass( + __METHOD__, + 'TopModel', + [ + [ + 'name' => 'inner', + 'visibility' => 'public', + ] + ], + false + ); + $views = new ViewsManager(); + + // when + $views->registerNamespace($firstNamespace, $firstNamespaceConfig); + $views->registerNamespace($secondNamespace, $secondNamespaceConfig); + + $innerModelClass = $secondNamespace . '\\InnerModel'; + $topModelClass = $firstNamespace . '\\TopModel'; + $topModel = new $topModelClass(); + $topModel->inner = new $innerModelClass(); + + // then + $this->assertSame('Hey inner!', $views->renderModel($topModel)); + } + + public function testRenderPassesInnerModelsFromDifferentNamespaces(): void + { + // given + vfsStream::setup('top', null, [ + 'folder1' => ['top-model.blade.php' => 'Hey {!! $inner !!}'], + 'folder2' => [ 'inner-model.blade.php' => 'inner!'], + ]); + $bladeRenderer = new ViewTemplateRenderer(); + $firstNamespaceConfig = (new ViewNamespaceConfig($bladeRenderer)) + ->setTemplatesRootPath(vfsStream::url('top/folder1')) + ->setTemplateFileExtension('.blade.php') + ->setModelsAsStringsInTemplates(true); $secondNamespaceConfig = (new ViewNamespaceConfig($bladeRenderer)) ->setTemplatesRootPath(vfsStream::url('top/folder2')) ->setTemplateFileExtension('.blade.php'); diff --git a/tests/pest/Unit/Model/ModelFactoryTest.php b/tests/pest/Unit/Model/ModelFactoryTest.php index 0ad5871..8ec31bf 100644 --- a/tests/pest/Unit/Model/ModelFactoryTest.php +++ b/tests/pest/Unit/Model/ModelFactoryTest.php @@ -10,6 +10,8 @@ use Prosopo\Views\Interfaces\Model\TemplateModelInterface; use Prosopo\Views\Interfaces\Object\ObjectReaderInterface; use Prosopo\Views\Interfaces\Object\PropertyValueProviderInterface; +use Prosopo\Views\Interfaces\Template\ModelTemplateResolverInterface; +use Prosopo\Views\Interfaces\Template\TemplateRendererInterface; use Prosopo\Views\PrivateClasses\Model\ModelFactory; class ModelFactoryTest extends TestCase @@ -19,18 +21,37 @@ public function testMakeModelCreatesInstanceOfModelClass(): void // given $objectReaderMock = Mockery::mock(ObjectReaderInterface::class); $propertyValueProviderMock = Mockery::mock(PropertyValueProviderInterface::class); - $factory = new ModelFactory($objectReaderMock, $propertyValueProviderMock); - - $modelClass = new class ($objectReaderMock, $propertyValueProviderMock) implements TemplateModelInterface { + $modelTemplateResolverMock = Mockery::mock(ModelTemplateResolverInterface::class); + $templateRendererMock = Mockery::mock(TemplateRendererInterface::class); + + $factory = new ModelFactory( + $objectReaderMock, + $propertyValueProviderMock, + $modelTemplateResolverMock, + $templateRendererMock + ); + + $modelClass = new class ( + $objectReaderMock, + $propertyValueProviderMock, + $modelTemplateResolverMock, + $templateRendererMock +) implements TemplateModelInterface { public ObjectReaderInterface $objectReader; public PropertyValueProviderInterface $propertyValueProvider; + public ModelTemplateResolverInterface $modelTemplateResolver; + public TemplateRendererInterface $templateRenderer; public function __construct( ObjectReaderInterface $objectReader, - PropertyValueProviderInterface $propertyValueProvider + PropertyValueProviderInterface $propertyValueProvider, + ModelTemplateResolverInterface $modelTemplateResolver, + TemplateRendererInterface $templateRenderer ) { $this->objectReader = $objectReader; $this->propertyValueProvider = $propertyValueProvider; + $this->modelTemplateResolver = $modelTemplateResolver; + $this->templateRenderer = $templateRenderer; } public function getTemplateArguments(): array @@ -46,6 +67,8 @@ public function getTemplateArguments(): array $this->assertInstanceOf(get_class($modelClass), $result); $this->assertSame($objectReaderMock, $result->objectReader); $this->assertSame($propertyValueProviderMock, $result->propertyValueProvider); + $this->assertSame($modelTemplateResolverMock, $result->modelTemplateResolver); + $this->assertSame($templateRendererMock, $result->templateRenderer); // apply Mockery::close(); @@ -55,8 +78,17 @@ public function testMakeModelThrowsExceptionForInvalidClass(): void { // given $objectReaderMock = Mockery::mock(ObjectReaderInterface::class); + $modelTemplateResolverMock = Mockery::mock(ModelTemplateResolverInterface::class); + $templateRendererMock = Mockery::mock(TemplateRendererInterface::class); + $propertyValueProviderMock = Mockery::mock(PropertyValueProviderInterface::class); - $factory = new ModelFactory($objectReaderMock, $propertyValueProviderMock); + + $factory = new ModelFactory( + $objectReaderMock, + $propertyValueProviderMock, + $modelTemplateResolverMock, + $templateRendererMock + ); // when $makeModel = fn() => $factory->createModel('NonExistentClass'); @@ -74,7 +106,15 @@ public function testMakeModelThrowsExceptionForInvalidConstructor(): void // given $objectReaderMock = Mockery::mock(ObjectReaderInterface::class); $propertyValueProviderMock = Mockery::mock(PropertyValueProviderInterface::class); - $factory = new ModelFactory($objectReaderMock, $propertyValueProviderMock); + $modelTemplateResolverMock = Mockery::mock(ModelTemplateResolverInterface::class); + $templateRendererMock = Mockery::mock(TemplateRendererInterface::class); + + $factory = new ModelFactory( + $objectReaderMock, + $propertyValueProviderMock, + $modelTemplateResolverMock, + $templateRendererMock + ); $invalidModelClass = new class (1) { public function __construct(int $test) { From 38d5c4c21fc7c77fd6a25d8950217d18a4d6b2dc Mon Sep 17 00:00:00 2001 From: Maxim Akimov Date: Wed, 18 Dec 2024 11:41:24 +0200 Subject: [PATCH 12/13] publish preparation: benchmark --- benchmark/src/Benchmark.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/src/Benchmark.php b/benchmark/src/Benchmark.php index 0e40b43..3026d4a 100644 --- a/benchmark/src/Benchmark.php +++ b/benchmark/src/Benchmark.php @@ -246,7 +246,7 @@ function ($templateFile) use ($twig, $templateArguments) { protected function defineModelClass(string $namespace, string $className): string { $code = sprintf( - 'namespace %s; class %s extends \Prosopo\Views\TemplateModel { public array $items; }', + 'namespace %s; class %s extends \Prosopo\Views\BaseTemplateModel { public array $items; }', $namespace, $className ); From a886ead2afc30af779c1952df3af499e44a88719 Mon Sep 17 00:00:00 2001 From: Maxim Akimov Date: Wed, 18 Dec 2024 11:55:15 +0200 Subject: [PATCH 13/13] readme --- README.md | 59 +++++++++++++++++++++++++++++---------------------- composer.json | 1 + 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index a52a33e..a6d2c0c 100644 --- a/README.md +++ b/README.md @@ -25,14 +25,23 @@ You're free to use the package in your own way: ## Table of Contents -- [1. Model-driven approach](#1-model-driven-approach) -- [2. Views Manager](#2-views-manager) -- [3. View Renderer](#3-view-renderer) -- [4. Benchmark](#4-benchmark) -- [5. Contribution](#4-contribution) -- [6. Credits](#5-credits) +- [1. Installation](#1-installation) +- [2. Model-driven approach](#2-model-driven-approach) +- [3. Views Manager](#3-views-manager) +- [4. View Renderer](#4-view-renderer) +- [5. Benchmark](#5-benchmark) +- [6. Contribution](#6-contribution) +- [7. Credits](#7-credits) -## 1. Model-driven approach +## 1. Installation + +`composer require prosopo/views` + +Don't forget to include the composer autoloader in your app: + +`require __DIR__ . '/vendor/autoload.php';` + +## 2. Model-driven approach Similar to many frameworks, such as Laravel, this package embraces a model-driven approach to templates. Each template is paired with its own Model, where the Model's public properties and methods act as arguments available within the @@ -78,7 +87,7 @@ Est. taxes: {{ $innerModel->taxes($salary) }} {!! $company !!} ``` -### 1.2) Benefits of the model-driven approach +### 2.1) Benefits of the model-driven approach 1. Typed variables: Eliminate the hassle of type matching and renaming associated with array-driven variables. 2. Reduced Routine: During object creation, public fields of the model without default values are automatically @@ -101,7 +110,7 @@ echo statements, which supports HTML output. For instance: If you prefer, you can configure the `ViewsManager` to pass models as strings instead of objects. Refer to the ` ViewsManager` section for details. -### 1.3) Custom property defaults +### 2.2) Custom property defaults Note: In the `TemplateModel` class, in order to satisfy the Model factory, the constructor is marked as final. If you need to @@ -132,7 +141,7 @@ class EmployeeTemplateModel extends BaseTemplateModel > automatically resolved > while object creation by your application's DI system. -### 1.4) Custom Model implementation (advanced usage) +### 2.3) Custom Model implementation (advanced usage) The only requirement for a Model is to implement the `TemplateModelInterface`. This means you can transform any class into a Model without needing to extend a specific base class, or even define public properties: @@ -165,7 +174,7 @@ $namespaceConfig->setModelsAsStringsInTemplates(true); When this option is enabled, the renderer will automatically convert objects implementing `TemplateModelInterface` into strings before passing them to the template. -## 2. Views Manager +## 3. Views Manager The `ViewsManager` class provides the `registerNamespace`, `createModel` and `renderModel` methods. It acts as a namespace manager and brings together different namespace configurations. @@ -174,7 +183,7 @@ Each `ViewNamespace` has its own independent setup and set of modules. E.g. amon `ModelTemplateProvider`, which automates the process of linking models to their corresponding templates. -### 2.1) Setup +### 3.1) Setup ```php use Prosopo\Views\View\ViewNamespaceConfig; @@ -214,7 +223,7 @@ $viewsManager->registerNamespace('MyPackage\Views', $namespaceConfig); // Tip: you can have multiple namespaces, and mix their Models. ``` -### 2.2) Single-step Model creation and rendering +### 3.2) Single-step Model creation and rendering You can create, set values, and render a Model in a single step using the callback argument of the `renderView` method, as shown below: @@ -231,7 +240,7 @@ echo $viewsManager->renderModel( This approach enables a functional programming style when working with Models. -### 2.3) Multi-step creation and rendering +### 3.3) Multi-step creation and rendering When you need split creation, use the factory to create the model, and then render later when you need it. @@ -260,7 +269,7 @@ the `ViewsManager` class itself. This approach ensures that only the specific actions you expect are accessible, promoting cleaner and more maintainable code. -### 2.4) Automated templates matching +### 3.4) Automated templates matching The built-in `ModelTemplateResolver` automatically matches templates based on the Model names and their relative namespaces. This automates the process of associating templates with their corresponding Models. @@ -284,7 +293,7 @@ names. > implement your own logic. In case the reason is the name-specific only, consider overriding the `ModelNameResolver` > module instead. -### 2.5) Custom modules +### 3.5) Custom modules By default, the `registerNamespace` class creates module instances for the namespace using classes from the current package. @@ -346,7 +355,7 @@ $namespaceConfig->getModules() > Note: The package includes only the Blade implementation. If you wish to use a different template engine, > like Twig, you need to install its Composer package and create a facade object, as demonstrated above. -### 2.6) Namespace mixing +### 3.6) Namespace mixing > Fun Fact: The `ViewsManager` class not only supporting multiple namespaces, but also enabling you to use Models from > one @@ -361,13 +370,13 @@ Additionally, you have a namespace for Twig templates, with a `Popup` model and Hereโ€™s the cool part: you can safely use `Button` as a property of the `Popup` model. The package will first render the `Button` using Twig, converting it to a string, and then pass it seamlessly into the Blade template of the `Popup`. -## 3. View Renderer +## 4. View Renderer `ViewTemplateRenderer` is the class responsible for rendering templates in this package. By default, it integrates the Blade compiler, but it is fully customizable. You can replace the Blade compiler with your own implementation or use a simple stub to enable support for plain PHP template files. -### 3.1) Built-in Blade integration +### 4.1) Built-in Blade integration [Blade](https://laravel.com/docs/11.x/blade) is an elegant and powerful template engine originally designed for [Laravel](https://laravel.com/). @@ -404,7 +413,7 @@ However, we chose not to adopt this approach for several reasons: Thanks to great Blade's conceptual design, our compiler implementation required fewer than 200 lines of code. -### 3.2) View Renderer setup +### 4.2) View Renderer setup ```php use Prosopo\Views\View\ViewTemplateRenderer; @@ -438,7 +447,7 @@ echo $viewTemplateRenderer->renderTemplate('@if($var)The variable is set.@endif' > means that even if you can't or don't want to use the model-driven approach, you can still utilize it as an > independent Blade compiler. -### 3.3) Available View Renderer settings +### 4.3) Available View Renderer settings The `ViewTemplateRenderer` supports a variety of settings that let you customize features such as escaping, error handling, and more: @@ -480,7 +489,7 @@ $viewRendererConfig = (new ViewTemplateRendererConfig()) $viewTemplateRenderer = new ViewTemplateRenderer($viewRendererConfig); ``` -### 3.4) Custom View Renderer modules +### 4.4) Custom View Renderer modules By default, the `ViewTemplateRenderer` creates module instances using classes from the current package, including the Blade compiler. @@ -528,7 +537,7 @@ $views->registerNamespace('MyApp\Models', $viewNamespaceConfig); Now this namespace is configured to deal with plain PHP template files, while having all the package features, including model-driven approach and template error handling. -## 4. Benchmark +## 5. Benchmark We conducted a [PHP performance benchmark](https://github.com/prosopo/php-views/blob/main/benchmark/src/Benchmark.php) to compare this package with the Laravel's Blade (mocked using [jenssegers/blade](https://github.com/jenssegers/blade)) @@ -557,13 +566,13 @@ repository, you can easily run it locally to verify the results. We encourage you to enhance the benchmark further - feel free to make it more advanced and submit a pull request. We're happy to review and accept contributions! ๐Ÿš€ -## 5. Contribution +## 6. Contribution We would be excited if you decide to contribute! Please read the [for-devs.md](https://github.com/prosopo/php-views/blob/main/for-devs.md) file for project guidelines and agreements. -## 6. Credits +## 7. Credits This package was created by [Maxim Akimov](https://github.com/light-source/) during the development of the [WordPress integration for Prosopo Procaptcha](https://wordpress.org/plugins/prosopo-procaptcha/). diff --git a/composer.json b/composer.json index d74ffef..5383e2b 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,7 @@ { "name": "prosopo/views", "description": "Blazing fast Views with model-driven approach, multi-namespace support and custom Blade implementation as a default template engine.", + "homepage": "https://github.com/prosopo/php-views", "keywords": [ "Views", "templates",