From 54a0ecc4db94352d39ac588cea57b8b36c2753a5 Mon Sep 17 00:00:00 2001 From: Can Vural Date: Mon, 21 Feb 2022 12:24:10 +0100 Subject: [PATCH 1/6] feat: remove empty lines in PhpContentExtractor --- src/Compiler/PhpContentExtractor.php | 25 ++++++++++++++++++- ...ariable_with_multiple_html_lines.blade.php | 1 - .../multiline_echo_with_html.blade.php | 1 - .../multine_html.blade.php | 23 +++++++++++++++++ 4 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 tests/Compiler/Fixture/PhpContentExtractor/multine_html.blade.php diff --git a/src/Compiler/PhpContentExtractor.php b/src/Compiler/PhpContentExtractor.php index 094c88b..55008c4 100644 --- a/src/Compiler/PhpContentExtractor.php +++ b/src/Compiler/PhpContentExtractor.php @@ -6,8 +6,11 @@ use function array_map; use function array_unshift; +use function explode; use function implode; +use function preg_match; use function preg_match_all; +use function rtrim; use function str_replace; use function str_starts_with; use function strip_tags; @@ -24,12 +27,15 @@ final class PhpContentExtractor */ private const PHP_OPEN_CLOSE_TAGS_REGEX = '#^(/\*\* file: .*?, line: \d+ \*/)(?!\s?/\*\* file: ).*?<\?php(.*?)\?>$#ms'; + private const TEMPLATE_FILE_NAME_AND_LINE_NUMBER_STRICT_REGEX = '#^(/\*\* file: .*?, line: \d+ \*/)$#m'; + /** * @param string $bladeCompiledContent This should be the string that is pre-compiled for Blade and then compiled by Blade. */ public function extract(string $bladeCompiledContent, bool $addPHPOpeningTag = true): string { $bladeCompiledContent = $this->removeHtmlTags($bladeCompiledContent); + $bladeCompiledContent = $this->removeEmptyLines($bladeCompiledContent); preg_match_all(self::PHP_OPEN_CLOSE_TAGS_REGEX, $bladeCompiledContent, $matches); @@ -41,7 +47,7 @@ public function extract(string $bladeCompiledContent, bool $addPHPOpeningTag = t $matches[1][$key] = $matches[1][$key - 1]; } - $phpContents = array_map(static fn ($a, $b) => $a . $b, $matches[1], $matches[2]); + $phpContents = array_map(static fn ($a, $b) => $a . rtrim($b), $matches[1], $matches[2]); if ($phpContents !== [] && $addPHPOpeningTag) { array_unshift($phpContents, '', '', $strippedInput); } + + private function removeEmptyLines(string $bladeCompiledContent): string + { + $lines = explode(PHP_EOL, $bladeCompiledContent); + + foreach ($lines as $key => $line) { + $trimmedLine = trim($line); + + if (! preg_match(self::TEMPLATE_FILE_NAME_AND_LINE_NUMBER_STRICT_REGEX, $trimmedLine)) { + continue; + } + + unset($lines[$key]); + } + + return implode(PHP_EOL, $lines); + } } diff --git a/tests/Compiler/Fixture/PhpContentExtractor/basic_variable_with_multiple_html_lines.blade.php b/tests/Compiler/Fixture/PhpContentExtractor/basic_variable_with_multiple_html_lines.blade.php index f0cb5b9..7fef813 100644 --- a/tests/Compiler/Fixture/PhpContentExtractor/basic_variable_with_multiple_html_lines.blade.php +++ b/tests/Compiler/Fixture/PhpContentExtractor/basic_variable_with_multiple_html_lines.blade.php @@ -3,5 +3,4 @@ ----- ----- + +
+ $foo is string. So it should error:
{{ $foo + 10 }}
+
+ +
+ {{-- We are overrding the $foo from before. Now it'll be integer only inside the included view. --}} + @include('included_view', ['foo' => 10,'bar' => $foo . 'bar']) +
+ +
+ $foo is string again here. So it should error:
{{ $foo + 20 }}
+ $bar was defined inside the include view. So it should error here:
{{ $bar }}
+
+ + +----- +make('included_view', ['foo' => 10,'bar' => $foo . 'bar'], \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); +/** file: foo.blade.php, line: 13 */ echo e($foo + 20); +/** file: foo.blade.php, line: 14 */ echo e($bar); \ No newline at end of file From 3f2fb0bf28bad66e39d843db840578f469422c29 Mon Sep 17 00:00:00 2001 From: Can Vural Date: Mon, 21 Feb 2022 12:24:35 +0100 Subject: [PATCH 2/6] feat: add ConvertArrayStringToArray class and tests --- src/PHPParser/ConvertArrayStringToArray.php | 85 +++++++++++++++++++ .../ConvertArrayStringToArrayTest.php | 47 ++++++++++ 2 files changed, 132 insertions(+) create mode 100644 src/PHPParser/ConvertArrayStringToArray.php create mode 100644 tests/PHPParser/ConvertArrayStringToArrayTest.php diff --git a/src/PHPParser/ConvertArrayStringToArray.php b/src/PHPParser/ConvertArrayStringToArray.php new file mode 100644 index 0000000..0bd1a00 --- /dev/null +++ b/src/PHPParser/ConvertArrayStringToArray.php @@ -0,0 +1,85 @@ + 'bar', 'bar' => 'baz']` to actual PHP array `['foo' => 'bar', 'bar' => 'baz']` + */ +final class ConvertArrayStringToArray +{ + private Parser $parser; + + public function __construct(private Standard $printer, private ConstExprEvaluator $constExprEvaluator) + { + $parserFactory = new ParserFactory(); + $this->parser = $parserFactory->create(ParserFactory::ONLY_PHP7); + } + + /** @return array */ + public function convert(string $array): array + { + $array = 'parser->parse($array); + + if ($stmts === null || count($stmts) !== 1) { + return []; + } + + if (! $stmts[0] instanceof Expression) { + return []; + } + + if (! $stmts[0]->expr instanceof Expr\Array_) { + return []; + } + + $array = $stmts[0]->expr; + assert($array instanceof Expr\Array_); + + $result = []; + + foreach ($array->items as $item) { + assert($item instanceof Expr\ArrayItem); + + if ($item->key === null) { + continue; + } + + $key = $this->resolveKey($item->key); + + if (! is_string($key)) { + continue; + } + + $value = $this->printer->prettyPrintExpr($item->value); + + $result[$key] = $value; + } + + return $result; + } + + private function resolveKey(Expr $expr): mixed + { + try { + return $this->constExprEvaluator->evaluateDirectly($expr); + } catch (ConstExprEvaluationException) { + return null; + } + } +} diff --git a/tests/PHPParser/ConvertArrayStringToArrayTest.php b/tests/PHPParser/ConvertArrayStringToArrayTest.php new file mode 100644 index 0000000..ccdbd79 --- /dev/null +++ b/tests/PHPParser/ConvertArrayStringToArrayTest.php @@ -0,0 +1,47 @@ + $expected + * + * @test + * @dataProvider greenProvider + * @dataProvider redProvider + */ + function it_can_convert_array_like_string_to_php_array(string $array, array $expected): void + { + $converter = new ConvertArrayStringToArray(new Standard(), new ConstExprEvaluator()); + + $this->assertSame($expected, $converter->convert($array)); + } + + public function greenProvider(): Generator + { + yield ["['foo' => 'bar', 'bar' => 'baz,bax']", ['foo' => "'bar'", 'bar' => "'baz,bax'"]]; + yield ["['foo' => \$foo . 'bar']", ['foo' => "\$foo . 'bar'"]]; + yield ["['foo' => \$foo->someMethod()]", ['foo' => '$foo->someMethod()']]; + } + + public function redProvider(): Generator + { + yield ['', []]; + yield ['123', []]; + yield ["'foo'", []]; + yield ['[]', []]; + yield ['[10]', []]; + yield ["['foo', 123]", []]; + yield ["[\$foo => 'bar']", []]; + } +} From e6a72b05ff0de12bc3eee3c7f2b24a04e789960b Mon Sep 17 00:00:00 2001 From: Can Vural Date: Mon, 21 Feb 2022 12:24:51 +0100 Subject: [PATCH 3/6] feat: add IncludedViewAndVariables value object --- src/ValueObject/IncludedViewAndVariables.php | 26 ++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/ValueObject/IncludedViewAndVariables.php diff --git a/src/ValueObject/IncludedViewAndVariables.php b/src/ValueObject/IncludedViewAndVariables.php new file mode 100644 index 0000000..4342abb --- /dev/null +++ b/src/ValueObject/IncludedViewAndVariables.php @@ -0,0 +1,26 @@ + $variablesAndValues */ + public function __construct(private string $includedViewName, private array $variablesAndValues) + { + } + + /** + * @return array + */ + public function getVariablesAndValues(): array + { + return $this->variablesAndValues; + } + + public function getIncludedViewName(): string + { + return $this->includedViewName; + } +} From 82d522e97e6c0ea2fca690abd167df58f0d11a7b Mon Sep 17 00:00:00 2001 From: Can Vural Date: Mon, 21 Feb 2022 12:26:16 +0100 Subject: [PATCH 4/6] fix: include variable scoping --- config/extension.neon | 3 +- src/Compiler/BladeToPHPCompiler.php | 56 +++++++++++++++++-- tests/Compiler/BladeToPHPCompilerTest.php | 5 +- tests/Rules/LaravelViewFunctionRuleTest.php | 34 +++++++++++ tests/Rules/data/laravel-view-function.php | 6 +- .../templates/file_with_include.blade.php | 17 ++++++ .../file_with_recursive_include.blade.php | 3 + tests/Rules/templates/included_view.blade.php | 2 + ...ome.blade.php => namespacedView.blade.php} | 0 9 files changed, 118 insertions(+), 8 deletions(-) create mode 100644 tests/Rules/templates/file_with_include.blade.php create mode 100644 tests/Rules/templates/file_with_recursive_include.blade.php create mode 100644 tests/Rules/templates/included_view.blade.php rename tests/Rules/templates/nested/directory/{home.blade.php => namespacedView.blade.php} (100%) diff --git a/config/extension.neon b/config/extension.neon index f27d80d..966fd22 100644 --- a/config/extension.neon +++ b/config/extension.neon @@ -37,4 +37,5 @@ services: - Vural\PHPStanBladeRule\Compiler\BladeToPHPCompiler - Vural\PHPStanBladeRule\Compiler\PhpContentExtractor - Vural\PHPStanBladeRule\PHPParser\NodeVisitor\BladeLineNumberNodeVisitor - - class: Vural\PHPStanBladeRule\Support\DirectoryHelper + - Vural\PHPStanBladeRule\PHPParser\ConvertArrayStringToArray + - Vural\PHPStanBladeRule\Support\DirectoryHelper diff --git a/src/Compiler/BladeToPHPCompiler.php b/src/Compiler/BladeToPHPCompiler.php index 64168a4..053ce1c 100644 --- a/src/Compiler/BladeToPHPCompiler.php +++ b/src/Compiler/BladeToPHPCompiler.php @@ -24,17 +24,26 @@ use Symplify\TemplatePHPStanCompiler\ValueObject\VariableAndType; use Throwable; use Vural\PHPStanBladeRule\Blade\PhpLineToTemplateLineResolver; +use Vural\PHPStanBladeRule\PHPParser\ConvertArrayStringToArray; use Vural\PHPStanBladeRule\PHPParser\NodeVisitor\AddLoopVarTypeToForeachNodeVisitor; use Vural\PHPStanBladeRule\PHPParser\NodeVisitor\RemoveEnvVariableNodeVisitor; use Vural\PHPStanBladeRule\PHPParser\NodeVisitor\RemoveEscapeFunctionNodeVisitor; +use Vural\PHPStanBladeRule\ValueObject\IncludedViewAndVariables; use Vural\PHPStanBladeRule\ValueObject\PhpFileContentsWithLineMap; +use function array_keys; +use function array_map; use function array_merge; use function getcwd; +use function implode; +use function in_array; use function preg_match_all; use function preg_quote; use function preg_replace; use function sprintf; +use function trim; + +use const PHP_EOL; final class BladeToPHPCompiler { @@ -63,10 +72,11 @@ public function __construct( private FileNameAndLineNumberAddingPreCompiler $preCompiler, private PhpLineToTemplateLineResolver $phpLineToTemplateLineResolver, private PhpContentExtractor $phpContentExtractor, + private ConvertArrayStringToArray $convertArrayStringToArray, private array $components = [], ) { $parserFactory = new ParserFactory(); - $this->parser = $parserFactory->create(ParserFactory::PREFER_PHP7); + $this->parser = $parserFactory->create(ParserFactory::ONLY_PHP7); // Disable component rendering $this->compiler->withoutComponentTags(); @@ -89,11 +99,13 @@ public function compileContent(string $filePath, string $fileContents, array $va $includes = $this->getIncludes($rawPhpContent); + $allVariablesList = array_map(static fn (VariableAndType $variableAndType) => $variableAndType->getVariable(), $variablesAndTypes); + // Recursively fetch and compile includes while ($includes !== []) { foreach ($includes as $include) { try { - $includedFilePath = $this->fileViewFinder->find($include); + $includedFilePath = $this->fileViewFinder->find($include->getIncludedViewName()); $includedFileContents = $this->fileSystem->get($includedFilePath); $preCompiledContents = $this->preCompiler->setFileName($includedFilePath)->compileString($includedFileContents); @@ -106,7 +118,31 @@ public function compileContent(string $filePath, string $fileContents, array $va $includedContent = ''; } - $rawPhpContent = preg_replace(sprintf(self::VIEW_INCLUDE_REPLACE_REGEX, preg_quote($include)), $includedContent, $rawPhpContent) ?? $rawPhpContent; + $usePlaceholder = 'use(%s)'; + + $includedContentPlaceHolder = << '$' . $key . ' = ' . $value . ';', array_keys($include->getVariablesAndValues()), $include->getVariablesAndValues())); + + $rawPhpContent = preg_replace(sprintf(self::VIEW_INCLUDE_REPLACE_REGEX, preg_quote($include->getIncludedViewName())), sprintf( + $includedContentPlaceHolder, + sprintf($usePlaceholder, implode(', ', array_map(static fn (string $variable) => '$' . $variable, $allVariablesList))), + $includedViewVariables, + $includedContent + ), $rawPhpContent) ?? $rawPhpContent; + + foreach ($include->getVariablesAndValues() as $variable => $value) { + if (in_array($variable, $allVariablesList, true)) { + continue; + } + + $allVariablesList[] = $variable; + } } $includes = $this->getIncludes($rawPhpContent); @@ -162,12 +198,22 @@ private function traverseStmtsWithVisitors(array $stmts, array $nodeVisitors): a return $nodeTraverser->traverse($stmts); } - /** @return string[] */ + /** @return IncludedViewAndVariables[] */ private function getIncludes(string $compiled): array { preg_match_all(self::VIEW_INCLUDE_REGEX, $compiled, $includes); - return $includes[1]; + $return = []; + + foreach ($includes[1] as $i => $include) { + $arrayString = trim($includes[2][$i], ' ,'); + + $array = $this->convertArrayStringToArray->convert($arrayString); + + $return[] = new IncludedViewAndVariables($include, $array); + } + + return $return; } private function setupBladeComponents(): void diff --git a/tests/Compiler/BladeToPHPCompilerTest.php b/tests/Compiler/BladeToPHPCompilerTest.php index e3fbc37..f30c1f3 100644 --- a/tests/Compiler/BladeToPHPCompilerTest.php +++ b/tests/Compiler/BladeToPHPCompilerTest.php @@ -8,6 +8,7 @@ use Illuminate\View\Compilers\BladeCompiler; use Illuminate\View\FileViewFinder; use Iterator; +use PhpParser\ConstExprEvaluator; use PhpParser\PrettyPrinter\Standard; use PHPUnit\Framework\TestCase; use Symplify\EasyTesting\DataProvider\StaticFixtureFinder; @@ -20,6 +21,7 @@ use Vural\PHPStanBladeRule\Compiler\BladeToPHPCompiler; use Vural\PHPStanBladeRule\Compiler\FileNameAndLineNumberAddingPreCompiler; use Vural\PHPStanBladeRule\Compiler\PhpContentExtractor; +use Vural\PHPStanBladeRule\PHPParser\ConvertArrayStringToArray; use Vural\PHPStanBladeRule\PHPParser\NodeVisitor\BladeLineNumberNodeVisitor; use function sys_get_temp_dir; @@ -47,7 +49,8 @@ protected function setUp(): void new FileViewFinder($fileSystem, $templatePaths), new FileNameAndLineNumberAddingPreCompiler($templatePaths), new PhpLineToTemplateLineResolver(new BladeLineNumberNodeVisitor()), - new PhpContentExtractor() + new PhpContentExtractor(), + new ConvertArrayStringToArray(new Standard(), new ConstExprEvaluator()), ); // Setup the variable names and types that'll be available to all templates diff --git a/tests/Rules/LaravelViewFunctionRuleTest.php b/tests/Rules/LaravelViewFunctionRuleTest.php index 79ed6a8..ef12c1e 100644 --- a/tests/Rules/LaravelViewFunctionRuleTest.php +++ b/tests/Rules/LaravelViewFunctionRuleTest.php @@ -7,6 +7,7 @@ use PHPStan\Rules\Cast\EchoRule; use PHPStan\Rules\Operators\InvalidBinaryOperationRule; use PHPStan\Rules\Rule; +use PHPStan\Rules\Variables\DefinedVariableRule; use PHPStan\Testing\RuleTestCase; use Symplify\TemplatePHPStanCompiler\PHPStan\FileAnalyserProvider; use Symplify\TemplatePHPStanCompiler\TypeAnalyzer\TemplateVariableTypesResolver; @@ -30,6 +31,7 @@ protected function getRule(): Rule [ self::getContainer()->getByType(InvalidBinaryOperationRule::class), self::getContainer()->getByType(EchoRule::class), + self::getContainer()->getByType(DefinedVariableRule::class), ], self::getContainer()->getByType(BladeViewMethodsMatcher::class), self::getContainer()->getByType(LaravelViewFunctionMatcher::class), @@ -73,6 +75,38 @@ public function testRule(): void 'Binary operation "+" between string and 10 results in an error.', 18, ], + [ + 'Variable $bar might not be defined.', + 18, + ], + [ + 'Binary operation "+" between string and 10 results in an error.', + 20, + ], + [ + 'Binary operation "+" between \'10bar\' and 30 results in an error.', + 20, + ], + [ + 'Binary operation "+" between string and 20 results in an error.', + 20, + ], + [ + 'Variable $bar might not be defined.', + 20, + ], + [ + 'Binary operation "+" between string and 10 results in an error.', + 22, + ], + [ + 'Binary operation "+" between \'10bar\' and 30 results in an error.', + 22, + ], + [ + 'Undefined variable: $bar', + 22, + ], ]); } diff --git a/tests/Rules/data/laravel-view-function.php b/tests/Rules/data/laravel-view-function.php index 5bf01c2..e56ac34 100644 --- a/tests/Rules/data/laravel-view-function.php +++ b/tests/Rules/data/laravel-view-function.php @@ -10,9 +10,13 @@ view('php_directive_with_comment', []); -view('dummyNamespace::home', ['variable' => 'foobar']); +view('dummyNamespace::namespacedView', ['variable' => 'foobar']); view('simple_variable') ->withFoo('bar') ->withBar(10); view('simple_variable')->with('foo', 'bar'); + +view('file_with_include', ['foo' => 'foo']); + +view('file_with_recursive_include', ['foo' => 'foo']); diff --git a/tests/Rules/templates/file_with_include.blade.php b/tests/Rules/templates/file_with_include.blade.php new file mode 100644 index 0000000..05d534a --- /dev/null +++ b/tests/Rules/templates/file_with_include.blade.php @@ -0,0 +1,17 @@ + + +
+ $foo is string. So it should error:
{{ $foo + 10 }}
+
+ +
+ {{-- We are overrding the $foo from before. Now it'll be integer only inside the included view. --}} + @include('included_view', ['foo' => 10,'bar' => $foo . 'bar']) +
+ +
+ $foo is string again here. So it should error:
{{ $foo + 20 }}
+ $bar was defined inside the included view. So it should error here:
{{ $bar }}
+
+ + \ No newline at end of file diff --git a/tests/Rules/templates/file_with_recursive_include.blade.php b/tests/Rules/templates/file_with_recursive_include.blade.php new file mode 100644 index 0000000..962be90 --- /dev/null +++ b/tests/Rules/templates/file_with_recursive_include.blade.php @@ -0,0 +1,3 @@ +{{ $foo + 10 }} + +@include('file_with_include', ['foo' => 10, 'baz' => 'foobar']) \ No newline at end of file diff --git a/tests/Rules/templates/included_view.blade.php b/tests/Rules/templates/included_view.blade.php new file mode 100644 index 0000000..04300cc --- /dev/null +++ b/tests/Rules/templates/included_view.blade.php @@ -0,0 +1,2 @@ +$foo is int here. So it should not error:
{{ $foo + 50 }}
+$bar is string
{{ $bar + 30 }}
\ No newline at end of file diff --git a/tests/Rules/templates/nested/directory/home.blade.php b/tests/Rules/templates/nested/directory/namespacedView.blade.php similarity index 100% rename from tests/Rules/templates/nested/directory/home.blade.php rename to tests/Rules/templates/nested/directory/namespacedView.blade.php From 77b9d016610d59bf0c1a1e5a3e0e2c84149a44b4 Mon Sep 17 00:00:00 2001 From: Can Vural Date: Mon, 21 Feb 2022 12:26:47 +0100 Subject: [PATCH 5/6] fix: update PHPStan baseline --- phpstan-baseline.neon | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index bb03a12..55d9446 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -35,13 +35,23 @@ parameters: count: 1 path: tests/Compiler/PhpContentExtractorTest.php + - + message: "#^Method Vural\\\\PHPStanBladeRule\\\\Tests\\\\PHPParser\\\\ConvertArrayStringToArrayTest\\:\\:greenProvider\\(\\) return type has no value type specified in iterable type Generator\\.$#" + count: 1 + path: tests/PHPParser/ConvertArrayStringToArrayTest.php + + - + message: "#^Method Vural\\\\PHPStanBladeRule\\\\Tests\\\\PHPParser\\\\ConvertArrayStringToArrayTest\\:\\:redProvider\\(\\) return type has no value type specified in iterable type Generator\\.$#" + count: 1 + path: tests/PHPParser/ConvertArrayStringToArrayTest.php + - message: "#^Parameter \\#1 \\$rules of class Vural\\\\PHPStanBladeRule\\\\Rules\\\\BladeRule constructor expects array\\\\>, array\\{PHPStan\\\\Rules\\\\Operators\\\\InvalidBinaryOperationRule\\} given\\.$#" count: 1 path: tests/Rules/BladeViewRuleTest.php - - message: "#^Parameter \\#1 \\$rules of class Vural\\\\PHPStanBladeRule\\\\Rules\\\\BladeRule constructor expects array\\\\>, array\\{PHPStan\\\\Rules\\\\Operators\\\\InvalidBinaryOperationRule, PHPStan\\\\Rules\\\\Cast\\\\EchoRule\\} given\\.$#" + message: "#^Parameter \\#1 \\$rules of class Vural\\\\PHPStanBladeRule\\\\Rules\\\\BladeRule constructor expects array\\\\>, array\\{PHPStan\\\\Rules\\\\Operators\\\\InvalidBinaryOperationRule, PHPStan\\\\Rules\\\\Cast\\\\EchoRule, PHPStan\\\\Rules\\\\Variables\\\\DefinedVariableRule\\} given\\.$#" count: 1 path: tests/Rules/LaravelViewFunctionRuleTest.php From ee5fdd40fbefd4fd95251d736517c7e8992b877d Mon Sep 17 00:00:00 2001 From: Can Vural Date: Mon, 21 Feb 2022 12:27:02 +0100 Subject: [PATCH 6/6] feat: require minimum PHPStan 1.4.6 --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index e5593f4..e0079d4 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "type": "phpstan-extension", "require": { "php": "^8.0", - "phpstan/phpstan": "^1.4.4", + "phpstan/phpstan": "^1.4.6", "illuminate/view": "^8.82 || ^9", "symplify/template-phpstan-compiler": "^10.0.20", "illuminate/filesystem": "^8.82 || ^9.0", @@ -13,7 +13,7 @@ "require-dev": { "phpunit/phpunit": "^9.5", "roave/security-advisories": "dev-latest", - "orchestra/testbench": "^6.24", + "orchestra/testbench": "^6.24 || ^7.0", "doctrine/coding-standard": "^9.0", "symplify/easy-testing": "^10.0" },