Whether you have direct push access to the repo or are planning to submit a pull request for some content changes, the issues list is a good place to start. If there is an issue that interests you, comment on the issue and sign up for it. If you want to add some new content or modify existing content that doesn't have an associated issue, please file an issue so we can keep track and go for it.
If you want to test your changes locally, the make sure you have followed the installation instructions for locally building the site.
There are three main content sections for this repo, including two guides and one API reference:
hhvm
is for user content on how to install, configure and use the HHVM runtime.hack
is for user content the features and tools associated with the Hack language.- There is a separate subsection for
hack
that is the API reference. This API reference is generated directly from the HHVM source code. But we also have the ability to provide examples for each API.
While we aren't 100% rigid on how we want contributions to come to us (we want to make contributing as easy as possible), there are some guidelines that we ask you follow when making contributions.
- Write great content :)
- For the guides, we try to remain semi-informal. Please use the second person (e.g., "you") to give the reader a feel they are being targeted directly.
- Focus on the user of Hack and HHVM; particularly, don't necessarily assume knowledge of a particular topic.
- Get your content reviewed by peers for accuracy and feedback. This only helps make better documentation for everyone.
- Code examples are awesome.
- Edit the SCSS source, instead of the generated
build/main.css
file.
- New topics or subtopics should follow the
##-topic/subtopic
naming convention. e.g.,22-async
or07-guidelines.md
. This is to allow documentation generation tooling an easier way to generate things like a table of contents. If necessary, you can rename/reorder topics or subtopics if it improves the logical flow. - With respect to the topic numbering and Hack:
- The
00-
topics are the more getting started topics. - The
20-
topics are the key feature topics. - The
40-
topics are additional topics. - There is one specialty numbering of
60-
for unsupported features.
- The
- Code must, obviously, be written in Hack, unless you are specifically writing some sort of comparison, anti-pattern, etc.
- Hack code must typecheck with
hh_client
correctly.
Code samples in all .md
files (guides as well as api-examples) should follow
this format:
```example.hack
echo "Hello world!\n";
```
The build script, when processing this .md
file, will extract the example code
and execute it via HHVM, then include both the example code and the output in
the rendered HTML.
The file name must be specified, so that the build script knows what name to use
for the extracted file. In almost all cases, the file extension should be
.hack
, unless the example is specifically about a feature that requires a
different extension (e.g. partial mode).
The build script automatically adds common boilerplate code to each extracted
example, e.g. the above echo
statement would be automatically wrapped in an
<<__EntryPoint>>
function.
Ideally, each code example should produce some output when HHVM runs it. For
example, a sample class definition would ideally be followed by an
<<__EntryPoint>>
function that demonstrates its usage.
However, if this would distract too much from the purpose of the guide, it's acceptable for some examples to not produce any output (e.g. an example class definition without usage). The extracted example file will still be typechecked and run, so at least we will still catch any typechecker and/or runtime errors.
In rare cases, when it's impossible to make a code sample into valid Hack code without making it too distracting, you can include a "fake" example code block that will not be typechecked and executed. To do this, simply omit the file name:
```
// Example code block without a file name will not be typechecked/executed.
// Avoid these if at all possible.
```
By default, the build script will put each extracted example into a unique namespace, so each example must be standalone and cannot depend on others (e.g. use a class declared in another example code block).
To override this, you can give multiple examples a shared prefix in their
filename followed by a period (.
):
Here's an example class:
```example_class.definition.hack
class C {}
```
And its usage:
```example_class.usage.hack
$c = new C();
```
The build script will ignore everything after the first period in the filename when generating the namespace name.
Note that this only works for examples in a single guide (single .md
file);
there is currently no way to put examples from separate guides into a shared
namespace.
HHVM requires an "autoloader" to be explicitly initialized whenever any Hack file references definitions from another Hack file.
The build script will insert the necessary initialization code automatically
into any <<__EntryPoint>>
function, so it is OK to rely on definitions from
other examples inside any <<__EntryPoint>>
function or functions called by it,
but not elsewhere.
For example, HHVM can never successfully run a file containing e.g. a class definition that references a parent class or other definition from another file (this is not a limitation specific to the docs site).
```example_hierarchy.parent.hack
abstract class Parent {}
```
```example_hierarchy.child.hack
// This file will NEVER successfully run in HHVM.
final class Child extends Parent {}
```
In practice, this is fine because running a file containing a class definition
is generally not needed. However, it does mean that trying to add an
<<__EntryPoint>>
function to example_hierarchy.child.hack
won't work,
because HHVM will fail with an "Undefined class Parent" error before it even
reaches it.
```example_hierarchy.child.hack
// This file will NEVER successfully run in HHVM.
final class Child extends Parent {}
<<__EntryPoint>>
function main(): void {
// This EntryPoint function is useless because HHVM will fail above.
}
```
The workaround is to put any code that depends on definitions from more than one other example into a separate code block.
```example_hierarchy.usage.hack
$c = new Child();
```
This can also be more convenient because we can rely on the automatic
boilerplate addition by the build script, instead of manually writing the
<<__EntryPoint>>
function header.
Examples that are expected to fail typechecking should use the .type-errors
extension:
```error_example.hack.type-errors
function missing_return_type() {}
```
The build script will run the Hack typechecker and include its output in the rendered HTML (instead of HHVM runtime output).
An example code block may specify additional files to be extracted alongside the example code using the following format:
```nondeterministic_example.hack
echo "Your lucky number is: ".\mt_rand(0, 99);
```.example.hhvm.out
Your lucky number is: 42
```.expectf
Your lucky number is: %d
```
Supported extensions are inherited from the HHVM test runner:
.hhconfig
if the example requires specific typechecker flags (e.g. demonstrating a feature that is not yet enabled by default).ini
for runtime flags.hhvm.expect
if you want to manually specify the expected output, instead of the build script doing it automatically.hhvm.expectf
to specify the expected output using printf-style syntax, like in the example above.expectregex
to specify the expected output using a regular expression.example.hhvm.out
should contain one possible output (this will be included in the rendered HTML instead of theexpectf
/expectregex
file; it is not needed for regularexpect
files).typechecker.expect
,.typechecker.expectf
,.typechecker.expectregex
,.example.typechecker.out
are the same but for typechecker (Hack) output instead of runtime (HHVM) output; they should only be included if the example code is expected to fail typechecking and you don't want the build script to generate them automatically.skipif
should contain valid Hack code that will print "skip" if the example should not be run (e.g. a MySQL example that should not run if there isn't a MySQL server running), otherwise print nothing
Adding a .md
file to the correct subdirectory in
api-examples
will cause the build script to add its content to the respective API page.
The .md
file may contain any combination of explanatory text and any number of
code examples following the same rules as above.
This should be rare, but if you need to generate markdown on the fly (e.g., we do this for generating the HHVM Supported PHP INI Settings at http://docs.hhvm.com/hhvm/configuration/INI-settings#supported-php-ini-settings.
- Create a
BuildStep
(PHP INI setting table example) for any data you might need to generate the markdown. - Create a
BuildStep
(PHP INI setting table example)for manually creating the markdown to be rendered.- The rendered markdown should be put in the
GUIDES_GENERATED_MARKDOWN
directory.
- The rendered markdown should be put in the
- Create a symlink (example) in the directory of the guide where the generated markdown will be rendered into. The symlink should point to
GUIDES_GENERATED_MARKDOWN
directory. - Update the actual guide that will have the generated markdown in it using the similar example syntax
@@
, but instead pointing to the.md
file that will be inserted in the guide (as opposed to a.php
) file. (example) - Add any test necessary (example).
Then our rendering process will add your generated markdown to your guide.
There are plenty of guide-to-guide links, guide-to-api links, etc. For internal links, we have a way to reference areas below. For external links, just use the absolute URL.
Images should be referenced in the user guide or example headers as /public/images/imagename.png
To reference the API documentation, use the following format:
- For classes:
/hack/reference/class/<class-name>/
- For interfaces:
/hack/reference/interface/<interface-name>/
- For traits:
/hack/reference/trait/<trait-name>/
- For methods:
/hack/reference/[class | interface | trait]/<class | interface | trait>-name/<method-name>/
- For functions:
/hack/function/<function-name>/
The names of classes, functions, etc. may be namespaced qualified (e.g., HH.class_meth
).
To reference guide documentation, use the following format:
- For HHVM:
/hhvm/<topic>/<sub-topic>
- For Hack:
/hack/<topic>/<sub-topic>
The topics and subtopics do not have their directory named numerical prefixes associated with them.
e.g., /hack/lambdas/creation-story
We have a test suite to ensure consistency across the changes we make to the guides, API references, and examples.
You can run it as follows:
$ vendor/bin/hacktest tests/
Nearly all of the code examples you see in the guides and API documentation are actual Hack or PHP source files that are embedded at site build time into the content itself.
As opposed to embedded the code examples directly within the markdown itself, this provides the flexibility of actually having running examples within this repo.
You must have HHVM installed in order to run these examples since most of them are written in Hack (e.g., <?hh
), and HHVM is the only runtime to currently support Hack.
You will find the examples in directories named with the pattern:
guides/[hhvm | hack]/##-topic/##-subtopic-examples
e.g.,
$ guides/hack/23-collections/06-constructing-examples
You can run any example standalone. For example:
# Assuming you are in the user-documentation repo directory
% cd guides/hack/23-collections/10-examples-examples/
% hhvm lazy.php
And you will see output like:
object(HH\Vector)#4 (5) {
[0]=>
int(0)
[1]=>
int(2)
[2]=>
int(4)
[3]=>
int(6)
[4]=>
int(8)
}
Time non lazy: 0.10859489440918
object(HH\Vector)#10 (5) {
[0]=>
int(0)
[1]=>
int(2)
[2]=>
int(4)
[3]=>
int(6)
[4]=>
int(8)
}
Time non lazy: 0.0096559524536133
Each example is structured to be run with the HHVM test runner. We use the test runner internally to ensure that any changes made to HHVM do not cause a regression. The examples in the documentation here can be used for that purpose as well.
You can run the HHVM test runner on the entire suite of examples, on one directory of examples or just one example itself.
Normally you will use our test suit described above to test any changes you make (because it tests our examples as well). However, sometimes it is actually faster and more explicit to test one example directly with the HHVM test runner.
# Assuming you are in the user-documentation repo root
# This runs every example in the test runner.
# Won't normally need to do this; just use our test suite instead.
# Test with the typechecker
$ api-sources/hhvm/hphp/test/run --hhserver-binary-path $(which hh_server) --typechecker guides/hack/05-statements/
# Test with the runtime
$ api-sources/hhvm/hphp/test/run --hhvm-binary-path $(which hhvm) guides/hack/05-statements/
Here is the output you should see when you run the test runner. Assume we are running the examples in the collections topic:
$ hhvm api-sources/hhvm/hphp/test/run guides/hack/23-collections/
Running 32 tests in 32 threads (0 in serial)
All tests passed.
| | |
)_) )_) )_)
)___))___))___)\
)____)____)_____)\
_____|____|____|____\\__
---------\ SHIP IT /---------
^^^^^ ^^^^^^^^^^^^^^^^^^^^^
^^^^ ^^^^ ^^^ ^^
^^^^ ^^^
Total time for all executed tests as run: 11.57s
You can use --verbose
to see all the tests that are running.