87 lines
3.2 KiB
PHP
87 lines
3.2 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
namespace MintyPHP\Tests\Architecture;
|
||
|
|
|
||
|
|
use PHPUnit\Framework\TestCase;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* MintyPHP's Router parses page filenames by splitting on parentheses. URL
|
||
|
|
* parameters belong ONLY in the `.php` action filename (e.g. `view($id).php`);
|
||
|
|
* the `.phtml` view filename must contain exactly ONE paren group for the
|
||
|
|
* template name (`view(default).phtml` or `view(none).phtml`).
|
||
|
|
*
|
||
|
|
* Nested paren groups like `view($id)(none).phtml` cause the router's regex
|
||
|
|
* split to produce an empty view name — the file silently resolves to the
|
||
|
|
* wrong path and the response is an empty body with no visible error.
|
||
|
|
*
|
||
|
|
* Reference: vendor/mintyphp/core/src/Router.php::extractParts()
|
||
|
|
*/
|
||
|
|
class PageTemplateNamingTest extends TestCase
|
||
|
|
{
|
||
|
|
use ProjectFileAssertionSupport;
|
||
|
|
|
||
|
|
public function testPhtmlFilenamesHaveExactlyOneParenGroup(): void
|
||
|
|
{
|
||
|
|
$root = $this->projectRootPath();
|
||
|
|
$scanRoots = [$root . '/pages'];
|
||
|
|
$modulesDir = $root . '/modules';
|
||
|
|
if (is_dir($modulesDir)) {
|
||
|
|
foreach (scandir($modulesDir) ?: [] as $moduleEntry) {
|
||
|
|
if ($moduleEntry === '.' || $moduleEntry === '..') {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
$modulePages = $modulesDir . '/' . $moduleEntry . '/pages';
|
||
|
|
if (is_dir($modulePages)) {
|
||
|
|
$scanRoots[] = $modulePages;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
$violations = [];
|
||
|
|
|
||
|
|
foreach ($scanRoots as $scanRoot) {
|
||
|
|
if (!is_dir($scanRoot)) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($scanRoot, \FilesystemIterator::SKIP_DOTS));
|
||
|
|
/** @var \SplFileInfo $file */
|
||
|
|
foreach ($iterator as $file) {
|
||
|
|
if (!$file->isFile() || $file->getExtension() !== 'phtml') {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
$name = $file->getFilename();
|
||
|
|
$openCount = substr_count($name, '(');
|
||
|
|
$closeCount = substr_count($name, ')');
|
||
|
|
$relativePath = str_replace($root . '/', '', $file->getPathname());
|
||
|
|
|
||
|
|
if ($openCount !== $closeCount) {
|
||
|
|
$violations[] = sprintf('%s — mismatched parens', $relativePath);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Partials (`_form.phtml`, shared includes) have zero paren groups — skip them.
|
||
|
|
if ($openCount === 0) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Routed views must have exactly one paren group: `<view>(<template>).phtml`.
|
||
|
|
if ($openCount !== 1) {
|
||
|
|
$violations[] = sprintf(
|
||
|
|
'%s — found %d paren groups, expected exactly 1 (template only). '
|
||
|
|
. 'URL parameters belong in the .php action filename, not the .phtml view. '
|
||
|
|
. "Nested paren groups break MintyPHP's Router silently (empty response).",
|
||
|
|
$relativePath,
|
||
|
|
$openCount
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->assertSame(
|
||
|
|
[],
|
||
|
|
$violations,
|
||
|
|
".phtml filename convention violations:\n" . implode("\n", $violations)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|