HHVM 4.64 is released! This release marks the end of support for 4.57; HHVM 4.58–4.62 remain supported (4.63 was skipped), as do the 4.32 and 4.56 LTS releases.

Breaking Changes

  • The INI flag hhvm.hack_arr_compat_specialization was removed and its behavior is now enabled by default. This change disables the implicit inter-operability between varrays and darrays. Instead, array (soon to be removed), varray and darray are now treated as types in their own right. Specifically:
    • Using a varray where a darray is expected (at parameter and return value typehints) throws, and vice versa. The same goes for property typehints, if they are checked at runtime.
    • varrays now maintain a “vec-like” layout, with integer keys from 0 to (size - 1). Attempting to set a string key in a varray throws, as does attempting to unset any element other than the last one.
    • Tuples and shapes are internally represented as varrays and darrays, respectively. As a result, darray is tuple(...) returns false and darray as tuple(...) throws. The same goes for varray is shape(...) and varray as shape(...).
    • Comparing a varray and a darray using == or === always returns false.
    • Comparing a darray with a varray, or two darrays using >, <, <=, >=, <>, <=> results in an exception. Two varrays can still be compared.
    • If property type enforcement is turned on, property type invariance rules for class hierarchies will apply to varray, darray and array. For example, a child class will not be able to redeclare a property with type varray<Tv> if the parent class’ property is typed darray<Tk, Tv>.
    • Additional related INI flags were removed, since they no longer have any effect:
      • hhvm.hack_arr_compat_type_hint_notices
      • hhvm.hack_arr_compat_check_varray_promote
      • hhvm.hack_arr_compat_check_implicit_varray_append
      • hhvm.hack_arr_compat_dv_cmp_notices
  • Attempting to override a non-abstract constant defined in an interface when implementing or extending that interface is now a typechecker error. It had already been a runtime error.
  • The function HH\global_keys() was removed. Other methods of enumerating all global variables, such as HH\global_get('GLOBALS') or $GLOBALS['GLOBALS'], are also expected to stop working soon.

Future Changes

  • We expect array(...) literals to be completely removed in the next release. All code will need to be migrated to varray[...] and darray[...], or preferably vec[...] and dict[...] (if it can be done safely).
    • Automated migration is available in HHAST v4.33.6 or newer: hhast-migrate --php-arrays
  • Any remaining methods of enumerating all global variables, such as HH\global_get('GLOBALS') or $GLOBALS['GLOBALS'], are expected to stop working in a future release.
  • We expect to remove the yield from syntax in a future release. In most cases, it can be replaced by other syntax.

Replacing yield from

Here’s a list of replacements to use based on the complexity of the use case:

If only the values, or only (key, value) pairs, are used, replace yield from with foreach...yield:

1
2
3
4
5
6
7
8
9
10
11
12
13
function only_values_old($gen) {
  yield from $gen;
}
function only_values_new($gen) {
  foreach ($gen as $v) { yield $v; }
}

function keys_values_old($gen) {
  yield from $gen;
}
function keys_values_new($gen) {
  foreach ($gen as $k => $v) { yield $k => $v; }
}

Use getReturn to evaluate the result of the yield from expression:

1
2
3
4
5
6
7
8
9
function return_old($gen) {
  $x = yield from $gen;
  var_dump($x);
}
function return_new($gen) {
  foreach ($gen as $v) { yield $v; }
  $x = $gen->getReturn();
  var_dump($x);
}

Use send to pass values back into the inner generator (if callers of the yield from outer generator do so). This use case is quite rare.

1
2
3
4
5
6
7
8
function send_old($gen) {
  yield from $gen;
}
function send_new($gen) {
  for ($gen->next(); $gen->valid(); $gen->send($s)) {
    $s = yield $gen->key() => $gen->current();
  }
}

There’s one behavior of yield from syntax that can’t be captured easily by rewrites like the above: if the same generator is aliased by a yield from and elsewhere, next() on that generator will advance the yield from. Such code will need a rewrite to make state explicit.