From c85281573371513e85597fdf71579e560879d5ff Mon Sep 17 00:00:00 2001 From: edalzell Date: Tue, 14 Jul 2026 13:19:31 -0700 Subject: [PATCH] Pass through arbitrary Expr nodes in TypeWriter, add castInt() helper TypeWriter::write() only passed FuncCall instances through untouched; any other manually-built PhpParser AST node (e.g. a Cast\Int_ for a literal (int) cast) fell through to TypeAnalyzer::typeOf(), which only understands scalars/arrays/null/bool/Closure and throws "Support type: object" for anything else. That made it impossible to embed a hand-built expression - like an (int) cast around an env() call - as a config value anywhere it'd end up nested inside an array (which is effectively everywhere, since ArrayWriter recurses through TypeWriter::write() for every item). FuncCall already extends PhpParser\Node\Expr, so broadening the passthrough check to any Expr is a strict generalization: everything that worked before (FuncCall) still works, and any other expression node - casts, ternaries, etc. - can now be used as a config value too. Added FunctionWriter::castInt() as a small fluent helper built on top of this, so callers get `(int) ...` the same way they already get env()/basePath()/etc., instead of reaching for raw PhpParser node construction: ConfigWriter::f()->castInt(ConfigWriter::f()->env('LIMIT', 20)) // => (int) env('LIMIT', 20) --- src/Writers/FunctionWriter.php | 7 +++++++ src/Writers/TypeWriter.php | 4 ++-- tests/LaravelFunctionCallTest.php | 13 +++++++++++++ tests/expected/castint.php | 5 +++++ 4 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 tests/expected/castint.php diff --git a/src/Writers/FunctionWriter.php b/src/Writers/FunctionWriter.php index 35f49c8..5619ac9 100644 --- a/src/Writers/FunctionWriter.php +++ b/src/Writers/FunctionWriter.php @@ -4,6 +4,8 @@ use Closure; use PhpParser\Node\Arg; +use PhpParser\Node\Expr; +use PhpParser\Node\Expr\Cast\Int_ as IntCast; use PhpParser\Node\Expr\ConstFetch; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Name; @@ -84,6 +86,11 @@ public function closure(Closure $closure) return $closure; } + public function castInt(Expr $expr): IntCast + { + return new IntCast($expr); + } + private function convertToArgs($args) { return collect($args) diff --git a/src/Writers/TypeWriter.php b/src/Writers/TypeWriter.php index aaa7e6b..562031f 100644 --- a/src/Writers/TypeWriter.php +++ b/src/Writers/TypeWriter.php @@ -5,10 +5,10 @@ use Closure; use Exception; use Laravel\SerializableClosure\Support\ReflectionClosure; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\Array_; use PhpParser\Node\Expr\Cast\Bool_; use PhpParser\Node\Expr\Cast\Int_; -use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Scalar\String_; use Stillat\Proteus\Analyzers\TypeAnalyzer; use Stillat\Proteus\Analyzers\Types; @@ -40,7 +40,7 @@ public static function write($value) return $closureParser->parse($serialized->getCode()); } - if ($value instanceof FuncCall) { + if ($value instanceof Expr) { return $value; } diff --git a/tests/LaravelFunctionCallTest.php b/tests/LaravelFunctionCallTest.php index 3e426a1..00b21ed 100644 --- a/tests/LaravelFunctionCallTest.php +++ b/tests/LaravelFunctionCallTest.php @@ -77,6 +77,19 @@ public function testTypesAreMaintained() ); } + public function testCastIntCanBeWritten() + { + $f = new FunctionWriter(); + + $this->assertChangeEquals( + __DIR__.'/configs/empty.php', + __DIR__.'/expected/castint.php', + [ + 'thing' => $f->castInt($f->env('KEY', 20)), + ] + ); + } + public function testWritingNewClosuresOrArrowFunctions() { $f = new FunctionWriter(); diff --git a/tests/expected/castint.php b/tests/expected/castint.php new file mode 100644 index 0000000..ca7d9a7 --- /dev/null +++ b/tests/expected/castint.php @@ -0,0 +1,5 @@ + (int) env('KEY', 20), +];