Skip to content
This repository was archived by the owner on Aug 25, 2025. It is now read-only.

v2.0: safer, simpler, and more powerful

Choose a tag to compare

@fredemmott fredemmott released this 16 Feb 01:27
0baa1ec

This release breaks compatibility to:

  • take advantage of modern features of Hack to detect common mistakes
  • replace special-cases with more generalized and powerful systems
  • better support per-site configuration, including base directory for 'generated by'

Global functions and configuration

In prior releases, the default HackCodegenConfig implementation would be used, and could not
be consistently overridden. Now, HackCodegenConfig takes root directory as a parameter, and the configuration can be completely be replaced by implementing IHackCodegenConfig, which is completely threaded through the stack. This required removing the global functions which implicitly used the global configuration object.

With v1.x

$x = codegen_class('MyClass');

With v2.x:

$config = new HackCodegenConfig(MyApp::getRootDir());
$cgf = new HackCodegenFactory($config);
$x = $cgf->codegenClass('MyClass');

Format Strings

In 1.x, some function would take a format string, however, if no extra arguments were passed, it would be treated as a plain string by some functions, but not all. This has been made consistent by adding f to the end of all functions that take a format string, and adding a version without the f that always takes a literal. Additionally, this allows the typechecker to check that format strings and parameters are valid.

With v1.x

$builder->add('foo bar'); // good
$builder->add('foo %s baz', 'bar'); // good
$builder->add('foo %s bar'); // inconsistent
$builder->add('%d', 'Not an int'); // accepted but bad

With v2.x

$builder->add('foo bar'); // good
$builder->add('foo %s baz', 'bar'); // typechecker error, too many arguments
$builder->addf('foo %s baz', 'bar'); // good
$builder->add('foo %s bar'); // good - always literally adds 'foo %s bar';
$builder->addf('%d', 'not an int'); // typechecker error, expected  an int

This also applies to other methods, e.g. addReturn() vs addReturnf().

Generating code for values

addVarExport(), addVector, addShape(), addArray(), and similar methods have been replaced with addValue(). Additionally, nested containers are now fully supported.

With v1.x

$builder->add($some_literal_value);
$builder->addVarExport($some_value_to_export);
$builder->addVector(
  Vector { 1, 2, 3 },
  HackBuilderValues::EXPORT,
);
$builder->addMap(
  Map { 'foo' => '$foo', 'bar' => '$bar' },
  HackBuilderKeys::EXPORT,
  HackBuilderValues::LITERAL,
);

With v2.x

$builder->addValue($some_literal_value, HackBuilderValues::literal());
$builder->addValue($some_value_to_export, HackBuilderValues::export());
$builder->addValue(
  Vector { 1, 2, 3 },
  HackBuilderValues::export(),
);
// ... alternatively (with line-wrapping support)...
$builder->addValue(
  Vector { 1, 2, 3 },
  HackBuilderValues::vector(
    HackBuilderValues::export()
  ),
);
$builder->addValue(
  Map { 'foo' => '$foo', 'bar' => '$bar' },
  HackBuilderValues::map(
    HackBuilderKeys::export(),
    HackBuilderValues::literal(),
  ),
);

// Not supported on v1.x:

$builder->addValue(
  Vector { Vector { '$foo', '$bar' } , Vector { '$bar', '$baz' } },
  HackBuilderValues::vector(
    HackBuilderValues::vector(
      HackBuilderValues::literal(),
    ),
  ),
);

Additionally, addAssignment() now supports all of the above:

$builder->addAssignment(
  '$somevar',
  123,
  HackBuilderValues::literal(),
);

$builder->addAssignment(
  '$somevar',
  Vector { 1, 2, 3 },
  HackBuilderValues::vector(HackBuilderValues::export()),
);

Shapes

There are now two ways to generate code for shapes, depending on if all members should be
rendered the same way:

$builder->addValue(
  shape('foo' => 'bar', 'herp' => 'derp'),
  HackBuilderValues::shapeWithUniformRendering(HackBuilderValues::export()),
);
$builder->addValue(
  shape(
    'foo' => $foo,
    'bar' => Vector { 'herp', 'derp' },
  ),
  HackBuilderValues::shapeWithPerKeyRendering(
    shape(
      'foo' => HackBuilderValues::literal(),
      'bar' => HackBuilderValues::vector(HackBuilderValues::export()),
    ),
  ),
);

Misc

  • container order is now preserved
  • PHPUnit is now used instead of a custom system