Upgrading from v1 to v2

Table of contents

  1. Host code
    1. allow() validates function names
    2. Editor
  2. Scripts
    1. String literals stay strings
    2. == never converts types
    3. + adds numbers only
    4. Comparisons need values of the same kind
    5. Writes no longer create intermediate arrays
    6. Assignment targets
    7. Number literals
    8. Reserved variable names
  3. New features

v2 hardens the sandbox and makes the language behave more like JavaScript. Most scripts keep working, but several changes can break existing scripts or host code. Go through this page before you upgrade, and lint your stored scripts with the new version.

composer require php-script/php-script:^2.0

PHP Script v2 requires PHP 8.4 or newer.

Host code

allow() validates function names

allow() now throws an InvalidArgumentException for

  • functions that do not exist,
  • namespaced names,
  • functions that can never be exposed (Engine::NEVER_ALLOWED, e.g. extract, ini_set, call_user_func, unserialize), and
  • functions that take a callback, such as array_map, array_filter or usort.

Allow functions that take callbacks with allowWithCallbacks(). A script can only pass functions as callbacks that you allowed as well:

// v1
$engine->allow('array_map', 'strtoupper');

// v2
$engine->allowWithCallbacks('array_map')->allow('strtoupper');

See Functions for details.

Editor

getDefinition() of the MonarchLanguageDefinitionService has a new loopKeywords key, and the tokenizer highlights break and continue as keywords. See Integrate Editor.

Scripts

String literals stay strings

A string literal that looks like a number is a string. In v1 it was turned into a number.

Expression v1 v2
'007' 7 '007'
'1.5' 1.5 '1.5'

== never converts types

== and != compare without type juggling, like === in JavaScript. Integers and floats with the same value are equal. Lists, maps and objects are compared by value.

Expression v1 v2
1 == '1' true false
10 == 10.0 false true
Two objects with the same properties false true

+ adds numbers only

+ adds two numbers. With any other operand it concatenates strings, as in JavaScript. Numeric strings from the context are strings too.

Expression v1 v2
count + 1 with count = '42' 43 '421'
'Hello ' + name 'Hello Ada' 'Hello Ada'

Lists, maps and objects without __toString() cannot be concatenated.

Comparisons need values of the same kind

<, >, <= and >= compare numbers with numbers, strings with strings and dates with dates. '10' > 9 was true in v1 and throws Cannot compare string with int in v2.

Writes no longer create intermediate arrays

a.b.c = 1 needs a.b to exist. In v1 missing levels were created.

// v2
a = {b: {}}
a.b.c = 1

Assignment targets

Assignments, ++ and -- need a variable or a path below a variable, such as a, a.b or a[0]. o.f().a = 2 throws Invalid assignment target.

Number literals

Integer literals that do not fit into a PHP integer, such as 99999999999999999999, are a parse error. v1 turned them into floats.

Reserved variable names

Variables cannot be named this, like a PHP superglobal (_GET, _POST, GLOBALS, …) or start with __ps_.

New features

v2 also adds features you may want to use after upgrading:

  • evaluate() for single expressions and run() with return values — Evaluating and Running
  • List and map literals ([1, 2], {a: 1}) — Statements
  • Operation and memory limits, and a deterministic mode with an allowlist of pure functions — Limits
  • Linter diagnostics with rules and severities, and an infinite-loop warning — Linting

The full list is in the changelog.