Partially-generated files are a major feature of Hack Codegen, allowing marked manual sections to be edited, while retaining the ability to use Hack codegen to rewrite the remainder of the file.

A common question is why this should be used instead of generating abstract parent classes or traits; while that is still an option, partially-generated files can have major benefits:

  • simplified class/trait hierarchy
  • “fill in the blanks” is simpler than “extend this class”
  • the generated code can be used immediately, and modified as needed; this is especially useful when the majority of implementations will not need any modifications, or when generating both a class and a unit test for that class at the same time
  • manual sections can be used outside of classes

There are some cases where it’s not possible to use techniques like superclasses and traits, such as when generating scripts:

1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/env hhvm
<?hh
/**
 * This file is partially generated. Only make modifications between BEGIN
 * MANUAL SECTION and END MANUAL SECTION designators.
 */

/* BEGIN MANUAL SECTION init */
// We can't use code from a class or function as we don't have an autoloader yet
require_once(__DIR__.'/../../../vendor/autoload.php');
/* END MANUAL SECTION */

There alternative approaches, however they generall require putting code into a string, which is usually less preferable than having it as real code.

One of the reasons that partially-generated files are generally discouraged with other frameworks is that major upgrades often require re-creating the files completely, then manually re-doing edits; This isn’t the case with Hack Codegen: if a new version of a file contains a manual section with the same ‘key’ as an old version, the old manual section is copiedi into the new manual section.

Creating Manual Sections

The most common way to create a manual section is to mark an entire function or method body as a manual section - this is done by calling ->setManualBody(true) on the function or method builder:

1
2
3
4
5
6
7
8
9
10
<?hh

$class = $factory
  ->codegenClass('SomeClass')
  ->addMethod(
    $factory
      ->codegenMethod('someMethod')
      ->setManualBody(true)
      ->setBody('myDefaultImplementation();');
  );

Manual sections created this way are automatically keyed with the class and method name - in this case, /* BEGIN MANUAL SECTION SomeClass::someMethod */.

Manual sections can also be created in the middle of a function body or in pseudo-main code when using HackBuilder:

1
2
3
4
5
6
7
8
9
10
11
12
<?hh

$file = $factory
  ->codegenFile('somefile.php')
  ->setPseudomainHeader(
    $factory
      ->codegenHackBuilder()
      ->startManualSection('mykey')
      ->addLine('require_once(\'vendor/autoload.php\');')
      ->endManualSection()
      ->getCode()
  );

Keys & Signatures

Keys must be unique within a file; they are used by Hack Codegen to put the code they contain in the appropriate place when a file is rewritten; this can include re-ordering them, adding more, or removing existing ones that are no longer needed.

Files created by Hack Codegen are signed by default; if a file is modified, the signature will not match, and Hack Codegen will refuse to modify the file - however, manual sections are excluded from this calculation.

Edit on GitHub