Skip to content

Pass through arbitrary Expr nodes in TypeWriter, add castInt() helper - #42

Merged
JohnathonKoster merged 2 commits into
Stillat:masterfrom
edalzell:fix/typewriter-expr-passthrough
Jul 19, 2026
Merged

Pass through arbitrary Expr nodes in TypeWriter, add castInt() helper#42
JohnathonKoster merged 2 commits into
Stillat:masterfrom
edalzell:fix/typewriter-expr-passthrough

Conversation

@edalzell

Copy link
Copy Markdown
Collaborator

Problem

TypeWriter::write() only passes FuncCall instances through untouched. Any other hand-built AST node — say a Cast\Int_ for a literal (int) cast — falls through to TypeAnalyzer::typeOf(), which only understands scalars/arrays/null/bool/Closure and throws:

Exception: Support type: object

the moment that node ends up nested inside an array (which is effectively everywhere, since ArrayWriter recurses through TypeWriter::write() for every item). So there was no way to write something like (int) env('LIMIT', 20) as a config value — only the function-call form, intval(env('LIMIT', 20)), worked, even though the two aren't the same syntax you actually want when replicating an existing config file's style.

Solution

FuncCall already extends PhpParser\Node\Expr, so broadening the passthrough check from instanceof FuncCall to instanceof Expr is a strict generalization — everything that worked before still works, and any other expression node (casts, ternaries, etc.) can now be used as a config value too.

Added FunctionWriter::castInt(Expr $expr): Cast\Int_ as a small fluent helper on top of that, matching the style of the existing env()/basePath()/etc. helpers:

ConfigWriter::f()->castInt(ConfigWriter::f()->env('LIMIT', 20));
// => (int) env('LIMIT', 20)

Testing

New case in LaravelFunctionCallTest asserting castInt(env(...)) renders as (int) env(...). Same 2 pre-existing, unrelated failures as before (closure param formatting); nothing else regressed.

Note: branches off master, independent of #41 (this PR doesn't depend on it, and vice versa) — I merged both locally into a scratch branch to confirm they work together, which they do.

edalzell and others added 2 commits July 14, 2026 13:19
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)
@JohnathonKoster
JohnathonKoster merged commit 9587f81 into Stillat:master Jul 19, 2026
13 checks passed
@edalzell
edalzell deleted the fix/typewriter-expr-passthrough branch July 19, 2026 19:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants