diff --git a/__docs/phpdoc/doc-base/scripts/check-examples.php b/__docs/phpdoc/doc-base/scripts/check-examples.php index 8a6aff35..a41078fb 100644 --- a/__docs/phpdoc/doc-base/scripts/check-examples.php +++ b/__docs/phpdoc/doc-base/scripts/check-examples.php @@ -225,4 +225,4 @@ function usage() { echo "Example: php {$_SERVER['argv'][0]} -p /cvs/phpdoc/en -t /tmp\n"; echo "Optional: -o to load files in EDITOR (" . $_SERVER['EDITOR'] . ") or -o /path/to/editor\n"; exit; -} \ No newline at end of file +} diff --git a/__docs/phpdoc/doc-base/scripts/move-examples-to-files.php b/__docs/phpdoc/doc-base/scripts/move-examples-to-files.php new file mode 100644 index 00000000..f000f1d8 --- /dev/null +++ b/__docs/phpdoc/doc-base/scripts/move-examples-to-files.php @@ -0,0 +1,93 @@ +\s*[\S\s]*#isU'); +define('SAMPLE_CODE_ROOT', __DIR__."/../../sample-code"); + +function get_next_filename_suffix(string $root, string $file_prefix): int { + $suffix = 1; + $fp_len = strlen($file_prefix); + $di = new RecursiveDirectoryIterator($root, + RecursiveDirectoryIterator::SKIP_DOTS); + $iter = new RecursiveIteratorIterator($di); + foreach ($iter as $file) { + $name = pathinfo($file, PATHINFO_FILENAME); + // if name is hack.array.empty1, this should return 2 + if (strpos($name, $file_prefix) !== false) { + // strpos should most likely be 0 usually, then add the length of + // the prefix to get the actual numerical suffix + $file_suffix = substr($name, strpos($name, $file_prefix) + $fp_len); + if (intval($file_suffix) >= $suffix) { + $suffix = intval($file_suffix) + 1; + } + } + } + return $suffix; +} + +function replace_inline_examples_with_files(string $xml_file): void { + if (!is_file($xml_file)) { + return; + } + $xml_content = file_get_contents($xml_file); + $matches = array(); + + // Find the main xml id of the file to assist in naming example files. + preg_match(REGEX_ID_FIND, $xml_content, $matches); + $filename_prefix = ""; + if (!empty($matches[1])) { + $filename_prefix = $matches[1]; + } else { + $filename_prefix = basename($xml_file); + } + + $suffix = get_next_filename_suffix(SAMPLE_CODE_ROOT, $filename_prefix); + + $matches = array(); + $count = preg_match_all(REGEX_PROGRAM_LISTING, $xml_content, $matches); + for ($i = 0; $i < $count; $i++) { + if (!empty($matches[1][$i])) { // ( location="[a-zA-Z0-9/]")? + continue; // We already have file location, assume it has been processed + } + // The (([\S\s]*)) in [CDATA\[([\S\s]*)\]\], i.e. the program + if (!empty($matches[2][$i])) { + $filename = SAMPLE_CODE_ROOT . "/" . $filename_prefix + . strval($suffix) . ".php"; + file_put_contents($filename, ltrim($matches[2][$i])); + $search = $matches[0][$i]; // The entire ... + $replace = ''; + $xml_content = str_replace($search, $replace, $xml_content); + $suffix++; + } + } + file_put_contents($xml_file, $xml_content); + return; +} + +function get_xml_files(string $root): Set { + $xml_files = new Set(); + $di = new RecursiveDirectoryIterator($root, + RecursiveDirectoryIterator::SKIP_DOTS); + $iter = new RecursiveIteratorIterator($di); + foreach ($iter as $file) { + $filename = $file->getPathname(); + if ($file->isFile() && pathinfo($file, PATHINFO_EXTENSION) === 'xml') { + $xml_files->add($filename); + } + } + return $xml_files; +} + +// Staring only with the Hack examples initially. +$root = __DIR__ . "/../../en/hack"; +$files = get_xml_files($root); +foreach ($files as $file) { + replace_inline_examples_with_files($file); +} +$root = __DIR__ . "/../../en/hackref"; +$files = get_xml_files($root); +foreach ($files as $file) { + replace_inline_examples_with_files($file); +} diff --git a/__docs/phpdoc/en/hack/annotations.xml b/__docs/phpdoc/en/hack/annotations.xml index 0697dc8f..4bc9b68a 100644 --- a/__docs/phpdoc/en/hack/annotations.xml +++ b/__docs/phpdoc/en/hack/annotations.xml @@ -4,38 +4,10 @@ Type annotations allow for PHP code to be explicitly typed on parameters, class member variables and return values (types are inferred for locals). These annotated types are checked via a type checker. Here are examples of the same code without and with type annotations: - - - + - - - + @@ -63,24 +35,7 @@ class MyClass { Introductory Example - - - + What should be noticeably different from PHP is that AnnotatedClass has type information on all member variables (whether public, private or protected), as well as its function parameters and return types. The annotated types in this example are: @@ -1380,32 +1335,7 @@ class AnnotatedClass { Most of the time, initialization of the class members will be done in a constructor (i.e., __construct()). For brevity, most of the initialization in the table above was done inline. However, sometimes brevity doesn't work very well. For the generic type, user object, nullable and closure class members, here are example initializations in the constructor: - -t = $val; - $this->a = new FooClass(); - $this->ni = $val === 3 ? null : 4; - $this->x = function(int $n, int $m): string { - $r = ''; - for ($i=0; $i < $n+$m; $i++) { - $r .= "hi"; - } - return $r; - }; - } -} -]]> - + @@ -1416,167 +1346,31 @@ class MyClass { Below are some basic, contrived examples using some of the above types within the context of the Hack type annotation framework: Annotating With Basic Types - - - + Annotating with void - - - + Annotating with Nullable - - - + Annotating with mixed - - - + Annotating Classes - - $arr): int { - $s = 0; - foreach ($arr as $v) { - $s += $v; - } - return $s; -} -]]> - + Annotating Tuples - - $test = Vector {}; - // The return type is a tuple. Again, the "tuple" reserved - // word is not used. - public function bar(): (string, string) { - return $this->test[0]; - } - public function foo() { - // But to use an actual tuple, use the "tuple" reserved word - $this->test->add(tuple('hello', 'world')); - } -} -]]> - + Annotating Resources - - - + @@ -1586,18 +1380,7 @@ function f3(): void { HHVM allows type casting, basically allowing for a variable to be cast to another, appropriate type (e.g., int to bool). Hack allows type casts as well. For example, the type checker will give no errors for this type of code. - - - + @@ -1674,18 +1457,7 @@ foo(); Here is an example of how the type checker will throw an error if you try to use a synonym of a type that is not supported. - - - + &example.outputs; Here is an example of how various arrays are annotated. Remember that, in Hack, the use of arrays are more restricted in // strict mode. - - $arr; - private array $arr2; - - public function __construct() { - $this->arr = array(); - $this->arr2 = array(); - } - - public function bar(T $val): array { - return array($val); - } - - public function sort(array $a): array { - sort($a); - return $a; - } - - public function baz(FooFoo $val): array { - $this->arr[] = $val; - return $this->arr; - } -} - -function main_aa() { - $haa = new HackArrayAnnotations(); - var_dump($haa->bar(3)); - var_dump($haa->bar(new FooFoo())); - var_dump($haa->sort(array(1.3, 5.6, 2.3, 0.2, 1.4))); - var_dump($haa->baz(new FooFoo())); -} - -main_aa(); -]]> - + &example.outputs; Examining a typed array a bit more... - - $arr; - private int $i; - - public function __construct() { - $this->arr = array(new BarBar()); - $this->i = 4; - } - public function getBars(): array { - if ($this->i < 5) { - return array(); - } else if ($this->i < 10) { - return $this->arr; - } else { - return array(null); // Type Error - } - } -} -]]> - + An empty array can be returned from a method that is annotated to return a typed array. However, an array with the first element null is not compatible. In order to make that work, a nullable typed array must be used as the annotation (e.g., : array<?BarBar>). @@ -1836,54 +1544,10 @@ class ABCD { Annotating closures and callables require their own callout beyond the brief summary above about using PHP types with Hack. Take this unannotated, non-Hack PHP code that uses a closure: - - - + How is the function foo_closure() and the closure function actually annotated? Here is the proper Hack type annotation for such a function: - - - + The return type annotation of foo_closure() is actually a skeleton signature of the actual closure being returned. Thus, for example, trying to return true from the closure will throw a Hack error since the return type annotation clearly specifies that the closure returns an int. Note that the actual closure is not type annotated, nor are any use parameters part of the type annotation for a closure. @@ -1895,28 +1559,7 @@ main_closure_example(); The same style of annotating closures are used in function/method parameters (if the function/method takes a closure as a parameter), as well as class member variables. Here is a final example: - - - + @@ -1936,41 +1579,7 @@ function f2(): string { The this type is a pretty useful type, which you'll usually see as a return type. Here are some examples of it being used: - -x = $new_x; - // $this has type "this" - return $this; - } - public static function newInstance(): this { - // new static() has type "this" - return new static(); - } - public function newCopy(): this { - // This would not type check with self::, but static:: is ok - return static::newInstance(); - } - // You can also say Awaitable; - public async function genThis(): Awaitable { - return $this; - } -} - -final class Child { - public function newChild(): this { - // This is OK because Child is final. - // However, if Grandchild extends Child, then this would be wrong, since - // $grandchild->newChild() should returns a Child instead of a Grandchild - return new Child(); - } -} -]]> - + this is the type of $this and new static(). If Base::setX() returns this, that means that at callsites, $child->setX() is known to return an instance of Child. @@ -1980,32 +1589,7 @@ final class Child { Here are some invalid uses of this: COUNTER EXAMPLES - -newBase() - // should return a Child, but it always returns a Base! - return new Base(); - } - - public static function newBase2(): this { - // ERROR! This is wrong for the same reason that new Base() is wrong - return new self(); - } - - // This function is fine - abstract public static function goodNewInstance(): this; - - public static function badNewInstance(): this { - // ERROR! Child::badNewInstance() would call Base::goodNewInstance() which is wrong - return self::goodNewInstance(); - } -} -]]> - + @@ -2021,21 +1605,7 @@ class Base { Sometimes the type of a function parameter or a return type can be "various". And this could be quite intentional. When confronted with this situation, there are two choices. One is to leave the type blank and let the Hack type checker assume the engineer knows what he/she is doing. The other is to use the PHP provided mechanism called mixed in order to have the type checker force the engineer to check the type before using it. The following example shows mixed being used as the parameter type and the subsequent needed if check. - - - + @@ -2045,30 +1615,7 @@ function sum(mixed $x): int { The Hack type checker largely does not understand references and pretends that they do not exist. For example, the following code passes the type checker: - - - + It seems pretty clear that this shouldn't be done (trying to swap an int but writing in a string, when the swap() method takes two ints.). In fact, HHVM will balk at runtime when trying to execute this code. But getting an error at runtime defeats the whole purpose of the benefits of Hack. @@ -2080,22 +1627,7 @@ main(); Why are these type of references bad? Consider the following PHP code snippet: - - - + After the foreach completes, there will still be a dangling reference to the last element in the array. So, if a developer then writes$k = 'foo';, the array will be mutated ... behavior probably not desired. @@ -2125,15 +1657,7 @@ array(4) { Generators can be somewhat tricky to type check. For example: - - - + @@ -2218,36 +1742,8 @@ function gen() { The following two examples are correctly typed: - - { - yield 1; - yield 2; - yield 3; -} - -function foo(): void { - foreach (gen() as $x) { - echo $x, "\n"; - } -} -]]> - - - { - return 42; -} - -async function g(): Awaitable { - $f = await f(); - $f++; - return 'hi test ' . $f; -} -]]> - + + diff --git a/__docs/phpdoc/en/hack/arrays.xml b/__docs/phpdoc/en/hack/arrays.xml index 76097753..db9e4cb6 100644 --- a/__docs/phpdoc/en/hack/arrays.xml +++ b/__docs/phpdoc/en/hack/arrays.xml @@ -40,38 +40,7 @@ Here is a code example expressing some of the Hack array constructs: - - { - private array $arr = array(); - private array $arr2 = array(); - private array $arr3 = array(); - - public function __construct(T $data) { - $this->arr2[0] = $data; - } - - public function bar(T $data): void { - $this->arr = array(); - var_dump($this->arr); - $this->arr2[] = $data; - var_dump($this->arr2); - $this->arr3["hi"] = new Foo(); - var_dump($this->arr3); - } -} - -function main_arr() { - $ha = new HackArrays("Facebook"); - $ha->bar("Food"); -} - -main_arr(); -]]> - + &example.outputs; To enable asynchronous programming in HHVM, the concept of creating async functions was used. The two HHVM PHP language keywords that enable async functions are async and await. async declares a function as asynchronous. await suspends the execution of an async function until the result of the asynchronous operation represented by await is available. The return value of a function that await can be used upon is an object that implements Awaitable<T>. - - { - if ($a === 0) { - return null; - } - - $bar = await gen_bar($a); - if ($bar !== null) { - return $bar->getFoo(); - } - - return null; -} - -async function gen_bar(int $a): Awaitable { - if ($a === 0) { - return null; - } - - return new Bar(); -} - - -gen_foo(4); -]]> - + gen_foo() is an async function as shown by the async keyword preceding the function signature. Async functions always return an object that implements Awaitable<T> (the normal concrete implementation is WaitHandle<T>, but it is almost always preferred to use the Awaitable<T> interface). @@ -87,20 +52,7 @@ gen_foo(4); Signature Examples ¬e.hack.async; - - { return new Foo(); } -async function cached_result(T $x): Awaitable { return $x; } -async function gen_void(): Awaitable { return; } -async function gen_add(Awaitable $genA, Awaitable $genB): Awaitable { - list($a, $b) = await genva($genA, $genB); - return $a + $b; -} -class Preparable implements Awaitable { ... } -class MyPreparable extends Preparable { ... } -]]> - + diff --git a/__docs/phpdoc/en/hack/attributes.xml b/__docs/phpdoc/en/hack/attributes.xml index 0c02d700..3e811780 100644 --- a/__docs/phpdoc/en/hack/attributes.xml +++ b/__docs/phpdoc/en/hack/attributes.xml @@ -34,23 +34,7 @@ Here is an example of the use of attributes: - -> - public function bar(string $key) { - return baz($key); - } -} - -$rc = new ReflectionClass('Foo'); -$attrs = $rc->getMethod('bar')->getAttributes(); -var_dump($attrs); -$attr = $rc->getMethod('bar')->getAttribute("key"); -var_dump($attr); -]]> - + &example.outputs; @@ -109,43 +93,11 @@ array(1) { the type checker cannot access the parent's method declarations. Consider the following: - -implementation(); - } - protected function implementation(): void { - echo 'parent implementation', "\n"; - } -} - -// file2.php -> - protected function implementation(): void { - echo 'child implementation', "\n"; - } -} -]]> - + If the parent class is (inadvertently) refactored to the following... - - - + ... the <<__Override>> annotation will result in a type checker error, alerting the refactorer of @@ -163,27 +115,7 @@ class CParent { overridden method exists is deferred to the classes that use the trait. - -> // checked on use classes - function foo(): void {} -} - -class C1 { - use T1; // error! foo is not an override -} - -class C2 { - function foo(): void {} -} - -class C3 extends C2 { - use T1; // OK! C2's implementation is being overridden -} -]]> - + @@ -197,22 +129,7 @@ class C3 extends C2 { __Memoize. Formerly, caching was done in this sort of manner: - -cache[$key])) { - $this->cache[$key] = function_that_returns_baz($key); - } - return $this->cache[$key]; - } -} -]]> - + This code employs an explicit caching mechanism to avoid possibly expensive function calls. @@ -223,18 +140,7 @@ class Foo { Now see how much simpler and succinct __Memoize makes the code above: - -> - public function bar(string $key): Baz { - return function_that_returns_baz($key); - } -} -]]> - + After the first call to bar(), the result will be automatically cached so that subsequent calls to @@ -264,24 +170,7 @@ class Foo { it is applied. It is not applied to other named functions in its class hierarchy. Here is an example: - -> - public function foo(string $key): Baz { - return another_function_that_returns_baz($key); - } -} -]]> - + @@ -310,24 +199,7 @@ class Child extends MParent { This attribute is useful particularly for new static() calls. Consider this example and the output of the Hack type checker: - - - + &example.outputs; - -> -class Foo { - public static function make(): this { - return new static(); - } -} -class ChildOfFoo extends Foo { - public function __construct(private A $a) {} -} - -function main() { - $child = ChildOfFoo::make(); -} -main(); -]]> - + &example.outputs; __ConsistentConstruct attribute, and the code is correct, the Hack type checker should be happy: - -> -class Foo { - public static function make(): this { - return new static(); - } -} -class ChildOfFoo extends Foo { - public function __construct() {} -} - -function main() { - $child = ChildOfFoo::make(); -} -main(); -]]> - + &example.outputs; The PHP language provides one primary mechanism for expressing containers of elements: the PHP array. Traditionally, the term "array" in programming languages is thought of as a collection of elements, indexed using consecutive integers (starting at 0 or 1), and possibly of a fixed size (e.g., Java). PHP arrays are significantly different than the traditional concept of arrays. A PHP array is essentially a dictionary-style collection associating keys with values that maintains insertion order. The keys can be either integers or strings; the values can be of any type. PHP arrays are dynamically-sized and grow as needed without requiring a function call to increase their size/capacity. In all Hack documentation, the term "array" will be considered to refer to a PHP array unless explicitly noted otherwise. Arrays in PHP are created using the array() language construct. Here are a few examples: - - "aaa", "b" => "bbb", "c" => "ccc"); - -// Array containing the values 1, 2, 3 but now indexed -// by a mix of integer keys and string keys -$arr4 = array("foo" => 1, 73 => 2, "bar" => 3); - -// Array having mixed-typed values, default indexed by -// integers. -$arr5 = array(1, "hello", array(2, 3), "goodbye"); - -// Dynamically grow arrays by just adding new values. The -// keys do not have to be sequential or of the same type. -$arr1[] = 4; // The key will be 3 -$arr1[4] = 5; -$arr2["bap"] = 6; -$arr3["d"] = "ddd"; -$arr4[] = "blah"; // The key will be 74 -$arr5[9] = 3; -]]> - + Even with the relatively simple examples above, it is easy to see that PHP arrays are different than the "arrays" in other popular programming languages. In addition to offering dictionary-like operations, PHP arrays also offer stack-like operations. For example, values on PHP arrays can be pushed, popped or shifted. @@ -52,15 +20,7 @@ $arr5[9] = 3; In Hack and PHP5, arrays are not objects. Hack collections are typed as objects. So, - - - + &example.outputs; Here is an example of using Hack collections: - -add(15); - $vector->add(20); - - $vector[] = 25; - - $vector->removeKey(2); - - foreach ($vector as $item) { - echo $item . "\n"; - } -} - -main_col(); -]]> - + &example.outputs; Here are some basic examples that show how Vector can be used: - -get(1) . "\n\n"; - - // Set value by key using "$c[$k]=$v" syntax, overwriting the previous - // value; note that "$c[$k]=$v" syntax for Vectors will throw an - // OutOfBoundsException if the key is out of bounds - $vector[0] = 999; - - // Remove an element by key - $vector->removeKey(2); - - // Iterate over the values using "foreach ($c as $v)" syntax - foreach ($vector as $v) { - echo $v . "\n"; - } - echo "\n"; - - // Iterate over the values using "for" and "$c[$x]" syntax - for ($i = 0; $i < count($vector); ++$i) { - echo $vector[$i] . "\n"; - } - - // Iterate over the keys and values using "foreach ($c as $k=>$v)" - // syntax - foreach ($vector as $k => $v) { - echo $k . ": " . $v . "\n"; - } - echo "\n"; -} - -main(); -]]> - + &example.outputs; - -filter(function($x) { return $x >= 50; }); - foreach ($filtered_vec as $key => $val) { - echo $key . " " . $val . "\n"; - } -} - -main(); -]]> - + &example.outputs; Here are some basic examples showing how Map can be used: - - 1, "B" => 2, "C" => 3}; - - // Add elements using "$c[$k]=$v" syntax; note that if $k is - // already present, "$c[$k]=$v" syntax will overwrite the previous - // value - $map["D"] = 4; - $map["E"] = 5; - - // Access value by key using "$c[$k]" syntax; note that "$c[$k]" - // syntax will throw an OutOfBoundsException if the key is not present - echo $map["A"] . "\n"; - - // Access value by key via get(); null will be returned if the key is - // not present - echo $map->get("B") . "\n\n"; - - // Remove element by key; if the key is not present the remove() - // method will do nothing and return - $map->remove("B"); - - // Testing if a key is present - echo ($map->contains("A") ? "true" : "false") . "\n\n"; - - // Iterate over the values using "foreach ($c as $v)" syntax - foreach ($map as $v) { - echo $v . "\n"; - } - - // Iterate over the keys and values using "foreach ($c as $k=>$v)" - // syntax - foreach ($map as $k => $v) { - echo $k . ": " . $v . "\n"; - } -} - -main(); // REMEMBER, insertion order is maintained. -]]> - + &example.outputs; - - 'a', 2 => 'b', 3 => 'c', 4 => 'd'}; - var_dump($m->filterWithKey(function($k, $v) { return $k >= 3; })); -} - -main(); -]]> - + &example.outputs; Here is a basic example showing how Set can be used: - -add("D")->add("E"); - - // Remove element by value - $set->remove("B"); - - // Testing if a value is present - echo ($set->contains("A") ? "true" : "false") . "\n\n"; - - // Iterate over the values using "foreach ($c as $v)" syntax - foreach ($set as $item) { - echo $item . "\n"; - } -} - -main(); -]]> - + &example.outputs; Here is an example of taking the difference between two sets using removeAll - -add(6); - $z = $s->RemoveAll($v); //difference between $v and $s - var_dump($s, $v, $z); -} - -foo(); -]]> - + &example.outputs; Here is a simple example showing how Pair can be used: - - - + &example.outputs; Hack collections has introduced a literal syntax in order to make it easy to create a new collection without the need for temporary arrays or helper functions. HHVM natively understands collection literals. Here is an example: - - - + The code above requires no hidden arrays or helper functions to build a Vector containing 1, 2, and 3. @@ -680,22 +436,7 @@ $vec = Vector {1, 2, 3}; Here are some basic examples showing how collection literal syntax can be used: - - 'foo', 73 => 'bar', 144 => 'baz'}; - var_dump($map); -} - -f(); - ]]> - + &example.outputs; - - 1, 'b' => 2}; - - // Each instance of class C will get its own copy - // of Vector {1, 2, 3} in property $foo - public $foo = Vector {1, 2, 3}; - - // Each invocation of h() with no parameters will - // return a distinct empty Vector - function h($x = Vector {}) { - return $x; - } - - function j() { - static $y = Map {1 => 'a', 2 => 'b'}; - return $y; - } -} - ]]> - + @@ -1003,23 +722,7 @@ class C { Collections are objects - - - + &example.outputs; Reference-like Semantics - - $value) { - echo $key . " => " . $value . "\n"; - if ($key == 0) { - $v1[2] = 9; - } - } -} - -main(); -]]> - + &example.outputs; Iterator Invalidation - - 1, 'b' => 2, 'c' => 3, 'd' => 4}; - foreach ($m as $key => $value) { - echo $key . " => " . $value . "\n"; - if ($key == 'a') { - $m->remove('d'); - } - } -} - -main(); -]]> - + &example.outputs; Key Not Present - -at(0)); - } catch (OutOfBoundsException $e) { - echo "Caught exception 2\n"; - } - - try { - var_dump($m->get(0)); - } catch (OutOfBoundsException $e) { - echo "Caught exception 3\n"; - } -} - -main(); -]]> - + &example.outputs; Const Interfaces - - $m, string $k): int { - echo $m[$k] . "\n"; -} - -function main() { - $m = Map {'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4}; - foo($m, 'c'); -} -]]> - + &example.outputs; <literal>keys()</literal> method - - 1, 'b' => 2, 'c' => 3, 'd' => 4}; - $v = $m->keys(); - var_dump($v); -} - -main(); -]]> - + &example.outputs; <literal>map()</literal> method - - 1, 'b' => 2, 'c' => 3, 'd' => 4}; - $m2 = $m->map(function(int $x):int { return $x + 10; }); - var_dump($m2); -} - -main(); -]]> - + &example.outputs; <literal>filter()</literal> method - - 1, 'b' => 2, 'c' => 3, 'd' => 4}; - $m2 = $m->filter(function(int $x):bool { return $x % 2 == 0; }); - var_dump($m2); -} - -main(); -]]> - + &example.outputs; Iterables - - 1, 'b' => 2, 'c' => 3, 'd' => 4}; - $result = $m->filter(function(int $x):bool { return $x % 2 == 0; }) - ->map(function(int $x):int { return $x + 1; }); - foreach ($result as $key => $value) { - echo $key . " => " . $value . "\n"; - } -} - -main(); -]]> - + &example.outputs; 5 Lazy Views - - 1, 'b' => 2, 'c' => 3, 'd' => 4}; - $iterable = $m->items(); - $m['a'] = 100; - $i = 0; - foreach ($iterable as $t) { - echo $t[0] . " => " . $t[1] . "\n"; - if ($i == 2) { - echo "Removing key 'a'\n"; - $m->remove('a'); - } - ++$i; - } -} -main(); - -]]> - + &example.outputs; <literal>Vector</literal> - -addAll(Vector {22, 33, 44}); - var_dump($vec); -} - -main(); -]]> - + &example.outputs; <literal>Map</literal> - - 11}; - $map->setAll(Map {'b' => 22, 'c' => 33}); - var_dump($map); -} - -main(); -]]> - + &example.outputs; Building a Concrete Collection - - 11, 'b' => 22, 'c' => 33}; - $v = new Vector($m); - var_dump($v); -} - -main(); -]]> - + &example.outputs; General Collection Interfaces - -(ConstCollection $in): OutputCollection { - $out = Vector {}; - if (!($in instanceof ConstVector)) { - return null; - } - foreach ($in->items() as $elm) { - if ($elm > 1) { - $out->add($elm); - } - } - return $out; -} - -function main(): void { - $x = Vector {1, 2, 3}; - var_dump(process_elements($x)); -} - -main(); -]]> - + &example.outputs; Generators - - $it) { .. } -function g() { yield 1; yield 2; } - -function main() { - $gen = g(); - foo($gen); -} - -main(); -]]> - + @@ -1525,100 +977,25 @@ main(); Real-world, existing code - - $y) { - if (array_key_exists($y, $g)) { - $ret[$x] = $g[$y]; - } - } - return $ret; -} -]]> - + Possible Indexish modification #1 - - ( - Indexish $f, - Indexish $g -): array { - $ret = array(); - foreach ($f as $x => $y) { - if (array_key_exists($y, $g)) { - $ret[$x] = $g[$y]; - } - } - return $ret; -} - ]]> - + Possible Indexish modification #2 - -( - Indexish $f, - Indexish $g -): Map { - $ret = Map {}; - foreach ($f as $x => $y) { - if (array_key_exists($y, $g)) { - $ret[$x] = $g[$y]; - } - } - return $ret; -} -]]> - + Test Code - - 1, 1 => 2, 2 => 3, 3 => 4, 4 => 5}; - $map2 = Map {0 => 6, 1 => 7, 2 => 8, 3 => 9, 4 => 10}; - var_dump(array_compose($map1, $map2)); // original - var_dump(array_compose($map1, $map2)); // modified - var_dump(map_compose($map1, $map2)); -} - -main_indexish() -]]> - + &example.outputs; Most PHP programmers are familiar with defining class properties. - -name = $name; - $this->age = $age; - } -} -]]> - + @@ -30,16 +16,7 @@ class Person { Introducing constructor argument promotion. This is a small, yet very useful syntatic improvement provided by the Hack language. Now instead of writing code like the example above, you can write: - - - + That's it. The class members will automatically be created and assigned to what is passed into the constructor. Syntatic sugar, yes. Typing and productivity win, for sure. diff --git a/__docs/phpdoc/en/hack/continuations.xml b/__docs/phpdoc/en/hack/continuations.xml index e4a96b7f..71d75902 100644 --- a/__docs/phpdoc/en/hack/continuations.xml +++ b/__docs/phpdoc/en/hack/continuations.xml @@ -7,22 +7,7 @@ Continuations are used in generator functions. They represent types that can be used with the PHP yield keyword. A function that is a continuation will return a type of Continuation<T>. - - { - $i = 0; - while (true) { - yield $i++; - } -} - -$generator = yieldInfiniteInts(); -foreach ($generator as $value) { - echo "$value\n"; -} -]]> - + diff --git a/__docs/phpdoc/en/hack/enums.xml b/__docs/phpdoc/en/hack/enums.xml index beab66c5..e1500978 100644 --- a/__docs/phpdoc/en/hack/enums.xml +++ b/__docs/phpdoc/en/hack/enums.xml @@ -10,25 +10,7 @@ Before first-class enums were available, the workaround to emulate an enum went something like this: - - - + Class constants would generally be used to mimic enumerations. Or, with Hack before first-class enums, @@ -41,25 +23,7 @@ function foo() { With first-class enums, declarations and usage are as familiar as other languages. - - - + @@ -74,17 +38,7 @@ function foo(): DayOfWeek { Enumerations are declared like: - - - + This creates a new type Foo, represented by int values. The enum constants @@ -92,15 +46,7 @@ enum Foo: int { Foo is a type and can be used as a type hint. - - - + @@ -115,39 +61,15 @@ function foo(): Foo { expected, you have to use constraints. - - - + This makes Foo a subtype of int, where this would work: - - - + ...but this would not work: - - - + diff --git a/__docs/phpdoc/en/hack/generics.xml b/__docs/phpdoc/en/hack/generics.xml index 354fcd26..49c293d0 100644 --- a/__docs/phpdoc/en/hack/generics.xml +++ b/__docs/phpdoc/en/hack/generics.xml @@ -4,22 +4,7 @@ Hack introduces generics to PHP (in the same vein as statically type languages such as C# and Java). Generics allow classes and methods to be parameterized (i.e., a type associated when a class is instantiated or a method is called). Here is an example: - - { - protected T $data; - - public function __construct(T $data) { - $this->data = $data; - } - - public function getData(): T { - return $this->data; - } -} -]]> - + @@ -28,22 +13,7 @@ class Box { method will return the same type as associated with Box at instantiation. Note, though, that the getData() method is not itself generic; it is just accessing the top-level type parameter of Box. A client use of Box would look like the following: - -getData()."\n"; - echo $gs->getData()."\n"; - echo $ga->getData()."\n"; -} - -main_gen(); -]]> - + &example.outputs; An example of a generic class is Vector<T>, from the Hack collections implementation. The T is what makes Vector generic, as it can hold any type of object, from int to a user-defined class. However, for any instantiation of the class, once a type has been associated with T, it cannot hold any other type. - - implements MutableCollection { -* : -* } -* -*/ - -function main_vec() { - $x = Vector {1, 2, 3, 4}; // T is associated with int - $y = Vector {'a', 'b', 'c', 'd'}; // T is associated with string -} - -main_vec(); -]]> - + $x is a Vector<int>, while $y is a Vector<string>. A Vector<int> and Vector<string> are not the same type. Methods can also be generic, even when the class is not. Normally, the situation is to have a generic class with non-generic methods or a non-generic class with generic methods. While it is possible, having a generic class with generic methods is usually the exception more than the rule. Having both a generic class and a generic method in that class might be useful when the method is something like a compare method and the generic type parameter to the method is thus different than the class. Here is a contrived example of a generic method in a non-generic class: - - { - public T $value; - public function __construct(T $v) { - $this->value = $v; - } -} - -class FooGenMethod { - public function swap(Box $a, Box $b) : void { - $temp = $a->value; - $a->value = $b->value; - $b->value = $temp; - } -} - -function main_genmeth() { - $f = new FooGenMethod(); - $y = new Box(3); - $z = new Box(4); - echo $y->value." ".$z->value; - $f->swap($y, $z); - echo $y->value." ".$z->value; -} - -main_genmeth(); -]]> - + The above example shows a generic method swap<T>() in a non-generic class FooGenMethod. @@ -183,32 +103,7 @@ main_genmeth(); Here is an example of a generic class: - -, Mailbox, Mailbox -class Mailbox { - private ?T $data; - - public function __construct() { - $this->data = null; - } - - public function put(T $mail): void { - $this->data = $mail; - } - - public function check(): ?T { - if ($this->data !== null) { - return $this->data; - } - return null; - } -} -]]> - + @@ -218,70 +113,7 @@ class Mailbox { Like classes, traits and interfaces can be generic as well. The guidelines are similar to that of classes. Here is an example: - - { - public function add(T $item): void; - public function remove(): T; -} - -// generic trait -trait Commerce { - public function buy(T $item): void { - echo 'Bought a '.get_class($item)."\n"; - } - - public function sell(T $item): void { - echo 'Sold a '.get_class($item)."\n"; - } -} - -// generic class that uses generic trait and implements generic interface -class BestBuy implements Box { - protected Vector $vec; - private int $last; - - public function __construct() { - $this->vec = Vector{}; - $this->last = -1; - } - - use Commerce; - - public function add(T $item): void { - $this->vec->add($item); - $this->last++; - } - - public function remove(): T { - $item = $this->vec->at($this->last); - $this->vec->removeKey($this->last--); - return $item; - } -} - -// For example purposes -abstract class Computer {} -class Apple extends Computer{} -class Lenovo extends Computer {} -class Dell extends Computer {} - -function main_gti() { - $store = new BestBuy(); - $store->add(new Lenovo()); - $store->add(new Apple()); - $store->add(new Dell()); - echo get_class($store->remove())."\n"; - $store->sell($store->remove()); -} - -main_gti(); -]]> - + &example.outputs; Here is an example of a generic method: - -(Box $a, Box $b) : T { - $temp = $a->value; - $a->value = $b->value; - $b->value = $temp; - return $temp; - } -} -]]> - + @@ -349,38 +168,7 @@ class FooGenMethod { Generics may have some surprising semantics when it comes to type inference. This code example will be used to discuss how generics and type inference are handled within Hack. - -add(new A()); - $x->add(new Z()); -} - -function foo_gi2(): void { - $x = Vector {}; - $x->add(new Z()); - $x->add(new A()); - foo_gi4($x); -} - -function foo_gi3(bool $b): void { - $x = Vector {}; - $x->add(new Z()); - $x->add(new A()); - foo_gi5($x); -} - -function foo_gi4(Vector $vec): void {} -function foo_gi5(Vector $vec): void {} -function foo_gi6(Vector $vec): void {} -]]> - + As probably expected, Hack allows an element of type Z to be added to a Vector<A>. Possibly surprisingly, the reverse is also true — to a point. Some programming languages require the type of the associated with the generic class be known at instantiation time. Hack, on the other hand, can begin determining the type of the generic upon first usage. @@ -405,49 +193,13 @@ function foo_gi6(Vector $vec): void {} Hack brings about a feature to override on return type in a particular circumstance. In essence, a method can be overridden by return type in a subclass if the return type is strictly compatible with the return type of the same method in the superclass. So, this works: - - - + Take a look at this seemingly similar example: - - { - $x = new Vector(); - $x->add(new FooG()); - return $x; - } -} - -class BBG extends AAG { - protected function bar(): Vector { - $x = new Vector(); - $x->add(new FooGChild()); - return $x; - } -} -]]> - + On the surface, it may seem like the above should be accepted by the Hack type checker. However, a closer look indicates otherwise. The parent, AAG has a bar() that returns a vector containing FooG. The child, BBG, has a bar() that returns a vector containing FooGChild. In other words, both return Vector, not individual instances of FooG or FooGChild. Thus, since the return type is a Vector, it is impossible to guarantee that the returned vector from BBG:bar() won't contain, for example, a mix of FooChild and FooG — even to a caller which called it as AAG::bar(). @@ -475,42 +227,7 @@ It is incompatible with an object of type FooGChild mixed is sometimes a source of confusion when it comes to compatibility with other types. This confusion can become exacerbated when it comes to generics. For example, should this pass the Hack type checker? - - { - private ?T $data; - - public function __construct() { - $this->data = null; - } - public function put(T $mail): void { - $this->data = $mail; - } - - public function check(): ?T { - if ($this->data !== null) { - return $this->data; - } - return null; - } -} - -function mbint(): Mailbox { - $mbi = new Mailbox(); - $mbi->put(3); - return $mbi; -} - -function mbmixed(Mailbox $mbm): void {} - -function main() { - $m = mbint(); - mbmixed($m); -} -]]> - + Reading the code, one might believe that passing a Mailbox<int> to a function that takes a Mailbox<mixed> should pass the type checker since mixed should be a superset of int. However, that belief would be incorrect. A Mailbox<int> is not a subtype of Mailbox<mixed>. @@ -519,52 +236,7 @@ function main() { Imagine if the code above was modified to look like this: - -, Mailbox, Mailbox -class Mailbox { - private ?T $data; - - public function __construct() { - $this->data = null; - } - public function put(T $mail): void { - $this->data = $mail; - } - - public function check(): ?T { - if ($this->data !== null) { - return $this->data; - } - return null; - } -} - -function mbint(): Mailbox { - $mbi = new Mailbox(); - $mbi->put(3); - return $mbi; -} - -function mbmixed(Mailbox $mbm): void { - // Put a string into the mixed Mailbox - $mbm->put("Hello"); -} - -function main() { - $m = mbint(); - // This function puts a string into the Mailbox - mbmixed($m); - // Now what was a Mailbox becomes a Mailbox. Probably not expected behavior. - var_dump($m); -} - -main(); -]]> - + Since generic objects are passed by reference, adding a string to the Mailbox<mixed> in mbmixed() actually transforms the Mailbox<int> in main() to one that can now take a string. This is probably not expected behavior and should not be able to pass the type checker. And, in fact, this code does not pass the Hack type checker! @@ -596,49 +268,7 @@ object(Mailbox)#1 (1) { Even though Hack is able to catch a T<int> to T<mixed> conversion attempt before runtime, a cleaner way to write the above code is to use a generic method: - -, Mailbox, Mailbox -class Mailbox { - private ?T $data; - - public function __construct() { - $this->data = null; - } - public function put(T $mail): void { - $this->data = $mail; - } - - public function check(): ?T { - if ($this->data !== null) { - return $this->data; - } - return null; - } -} - -function mbint(): Mailbox { - $mbi = new Mailbox(); - $mbi->put(3); - return $mbi; -} - -function mbgen(Mailbox $mbm, T $item): void { - $mbm->put($item); -} - -function main() { - $m = mbint(); - mbgen($m, 4); - var_dump($m); -} - -main(); -]]> - + @@ -648,52 +278,7 @@ main(); Generic class and methods can have restrictions to the types that are able to be used for the parameterized type argument upon instantiation or call, respectively. These are called constraints on type parameters. Here is an example: - - { - private T $id; - public function __construct(T $id) { - $this->id = $id; - } - public function getID(): T { - return $this->id; - } -} - -interface IFoo {} - -class CA {} -class CB extends CA {} - -class Bar implements IFoo {} -class Baz implements IFoo {} -class Biz {} - -final class AnyIdentity extends Identity {} -final class CAIdentity extends Identity {} -final class CBIdentity extends Identity {} -final class FooIdentity extends Identity {} - -function main_constraints(): void { - $ai = new AnyIdentity("Hello"); - $ai2 = new AnyIdentity(new Biz()); - - $cb = new CBIdentity(new CB()); - $cb2 = new CBIdentity(new CA()); // HACK ERROR! - - $ca = new CAIdentity(new CA()); - $ca2 = new CAIdentity(new CB()); - - $fi = new FooIdentity(new Bar()); - $fi2 = new FooIdentity(new Baz()); - $fi3 = new FooIdentity(new Biz()); // HACK ERROR! -} - -main_constraints(); -]]> - + @@ -719,11 +304,7 @@ main_constraints(); For the above example, there will be two errors thrown by the Hack type checker. The first is on this line of code: - - - + &example.outputs; The second Hack error is on this line of code: - - - + &example.outputs; Here is an example of a constraint on a generic method: - -(T $x): T { - return $x; - } -} - -function main_constraints_gm(): void { - $bip = new Bip(); - $bip->get(new Faz()); - $bip->get(new Far()); - $bip->get(new NoIFaz()); // Hack error; -} - -main_constraints_gm(); -]]> - + The generic method get() has a constraint that only types that implement IFaz (or their children!) may be used as a the type parameter T to the method. Since the class Far implements IFar and IFar implements IFaz, a Far is able to be passed to get(). However, a NoIFaz is not able to be passed to get() since it has no chain that leads to an IFaz. Here is the Hack error that will occur: @@ -815,49 +367,7 @@ Considering the constraint on the type 'T' A possible question that one may have when reading about constraints is, "Why have a constraint? Why not just have the class in question extend the class that is being used as the constraint?" Here is an example that addresses that question: - - { - private T $data; - public function __construct(T $x) { - $this->data = $x; - } - public function get(): T { - return $this->data; - } -} - -class BoxOfA extends Box { - private int $sum = 0; - public function __construct(T $e) { - parent::__construct($e); - $this->sum += $e->getVal(); - } -} - -function main_con(): void { - $b = new B(); - $box = new BoxOfA($b); - $b2 = $box->get(); - $b2->foo(); -} -]]> - + &example.outputs; Constraints can remedy this situation. Making a few small changes to the example, now a BoxOfA is constrained to taking an A or its children! So when a B (a child of A) is passed to BoxOfA, a B is returned when calling $box->get(). Thus, $b2->foo() is a perfectly legitimate call. No Hack errors! - - { - private T $data; - public function __construct(T $x) { - $this->data = $x; - } - public function get(): T { - return $this->data; - } -} - -// class BoxOfA extends Box -class BoxOfA extends Box { - private int $sum = 0; - public function __construct(T $e) { - parent::__construct($e); - $this->sum += $e->getVal(); - } -} - -function main_con(): void { - $b = new B(); - var_dump($b); - $box = new BoxOfA($b); - var_dump($box); - $b2 = $box->get(); - var_dump($b2); - var_dump($b2->foo()); -} - -main_con(); -]]> - + @@ -993,24 +453,7 @@ main_con(); Here is a covariance example: - - { - public function __construct(T $t) {} -} -class A {} -class B extends A {} - -function foo(MyClass $x): void {} - -function bar(): void { - $b = new B(); - $x = new MyClass($b); - foo($x); -} -]]> - + foo() can be called with any object assignment compatible with A. Since B is a @@ -1021,24 +464,7 @@ function bar(): void { Here is a contravariance example: - - { - public function __construct(T $t) {} -} -class A {} -class B extends A {} - -function foo(MyClass $x): void {} - -function bar(): void { - $a = new A(); - $x = new MyClass($a); - foo($x); -} -]]> - + foo() can be called with any object reverse assignment compatible with B. Since A @@ -1049,25 +475,7 @@ function bar(): void { Let's change the first example a bit so it doesn't work: - - { - public function __construct(T $t) {} -} -class A {} -class B extends A {} -class C {} - -function foo(MyClass $x): void {} - -function bar(): void { - $c = new C(); - $x = new MyClass($c); - foo($x); -} -]]> - + &example.outputs; With generics, it is useful to discuss the concept of open and closed types. A type is open if it is a type parameter (e.g. T). Obviously enough, a type is closed if it is not open (e.g. int) . Here is an example of using open and closed types when it comes to generics: - - { - - public function getOpen(Vector $vec): T { - return $vec[0]; - } - - public function getClosed(Vector $vec): int { - return $vec[0]; - } -} -]]> - + The method getOpen() takes an open type and returns an open type. The method getClosed() takes a closed type and returns a closed type. @@ -1116,27 +509,7 @@ class Foo { What happens, though, when a method takes an open type and tries to return a closed type using that open type? Take this example using two generic methods: - -(T $a): void { - echo "foo()"; -} -function bar(T $a): int { - echo "bar()"; - return $a; -} - -function main_oct() { - $x = 5; - foo($x); - bar($x); -} - -main_oct(); -]]> - + &example.outputs; Since bar() takes an open type argument, $a is not guaranteed to be compatible with an int. So it cannot be returned as an int, even if $a is an int when passed in. foo() returns void. Thus, as written, foo() can type check correctly. However, what happens if $a is attempted to be used as an int within foo()? - -(T $a): void { - $x = $a + 10; - echo "foo()"; -} -/*function bar(T $a): int { - echo "bar()"; - return $a; -}*/ - -function main_oct() { - $x = 5; - foo($x); - //bar($x); -} - -main_oct(); -]]> - + &example.outputs; - -Hello ".htmlize($name).""; -} -]]> - + In 2009, a PHP compiler called HipHop was released. In 2010, minor changes to the PHP language were introduced by the HPHP team to improve development time and provide basic type safety. The changes were XHP and parameter type constraints. - -Hello {$name}; -} -]]> - + diff --git a/__docs/phpdoc/en/hack/lambda.xml b/__docs/phpdoc/en/hack/lambda.xml index 95790606..d50214ea 100644 --- a/__docs/phpdoc/en/hack/lambda.xml +++ b/__docs/phpdoc/en/hack/lambda.xml @@ -7,20 +7,7 @@ In PHP 5.3 the closures feature was introduced, which allowed the programmer to write anonymous functions like so: - - - + &example.outputs; Here are two simple examples of how to use lambda expressions. You can find more examples here. - - $a + $b; - // Do other stuff - // ..... - return $fn(10); -} - -var_dump(lam(42)); // return 52 -]]> - + - - {$b = mult($b); return ($a + $b);}; - // Do other stuff - // ..... - return $fn(10); -} - -function mult(int $x): int { - return $x * 4; -} -var_dump(lam(42)); // return 178 -]]> - + @@ -101,18 +54,7 @@ var_dump(lam(42)); // return 178 A lambda expression is denoted in the source code by using the ==> binary operator, aka the lambda arrow. The left hand argument of the lambda arrow is the parameter list, and the right hand argument is either an expression or a curly brace-enclosed list of statements. - - $x . $y; -} -$fn = foo(); -echo $fn('baz'); // Outputs barbaz -]]> - + &example.outputs; Lambda expressions were designed to allow for extra brevity in common cases. In most cases the parameter list on the left hand side must be wrapped in parentheses, however if no return type is being specified AND there is exactly one parameter with no type hint and no default value then the parentheses can be omitted. Also when the right hand side is just a single expression, the result of expression will be returned as the return value when the Closure object is invoked, which allows the programmer to avoid having to type "return"explicitly in many cases. The example above is equivalent to the following: - - { return $x . $y; }; -} -$fn = foo(); -echo $fn('baz'); // Outputs barbaz -]]> - + &example.outputs; The ==> operator has relatively low precedence compared with other operators. In example #2 above, the ==> has lower precedence than the . operator, so the second line of function foo() is parsed as return ($y ==> ($x . $y));. This is convenient because it allows lambda expressions to have complex right hand sides without the need to wrap the right hand side in parentheses in many cases. Also, the ==> operator is right associative and if desired can be chained together, for example: - - $y ==> $x + $y; -$g = $f(7); -echo $g(4); // Outputs 11 -]]> - + &example.outputs; Expression-like lambdas with a single argument: - - $x + 1; -$foo(12); // returns 13 - -$squared = array_map($x ==> $x*$x, array(1,2,3)); -var_dump($squared); // $squared is array(1,4,9) -]]> - + Expression-like lambdas with no arguments or more than one argument require parentheses around the parameter list: - - 73; -$foo(); // returns 73 - -$bar = ($x,$y) ==> $x + $y; -var_dump($bar(3,8)); // outputs 11 -]]> - + A compound statement can be given as the body of lambda expression by using curly braces like so: - - { - echo "Map $name has:\n"; - foreach ($x as $k => $v) { - echo " $k => $v\n"; - } -}; -$dump_map( - "My Map", - Map {'a' => 'b', 'c' => 'd'}, -); -]]> - + Variables are captured automatically and transitively (including $this): - - $y ==> $x * $z + $y; -$bar = $foo(5); -var_dump($bar(4)); // outputs 59 -]]> - + Use parentheses if you need to provide parameter or return type hints, or if you need to provide default values for parameters: - - $captured . $k; -]]> - + diff --git a/__docs/phpdoc/en/hack/methoddispatch.xml b/__docs/phpdoc/en/hack/methoddispatch.xml index a68abfeb..110fd819 100644 --- a/__docs/phpdoc/en/hack/methoddispatch.xml +++ b/__docs/phpdoc/en/hack/methoddispatch.xml @@ -7,77 +7,7 @@ The code associated with the class instance method matrix is represented here: - -f1(); - $a::f1(); - A::f2(); - $a->f2(); - $a::f2(); - - self::f1(); - B::f1(); - self::f2(); - B::f2(); - - C::f3(); - $c->f3(); - $c::f3(); - C::f4(); - $c->f4(); - $c::f4(); - - parent::f1(); - parent::f2(); - - static::f1(); - static::f2(); - - $this->f1(); - $this->f2(); - } -} - -class C { - public function f3(): void { - echo "C.f3()\n"; - } - public static function f4(): void { - echo "C.f4()\n"; - } -} - -function main() { - $b = new B(); - $b->test(); -} - -main(); -]]> - + And here is the output of the calls made in the instance test() function of class B. @@ -720,77 +650,7 @@ main(); The code associated with the class static method matrix is represented here: - -f1(); - $a::f1(); - A::f2(); - $a->f2(); - $a::f2(); - - self::f1(); - B::f1(); - self::f2(); - B::f2(); - - C::f3(); - $c->f3(); - $c::f3(); - C::f4(); - $c->f4(); - $c::f4(); - - parent::f1(); - parent::f2(); - - static::f1(); - static::f2(); - - $this->f1(); - $this->f2(); - } -} - -class C { - public function f3(): void { - echo "C.f3()\n"; - } - public static function f4(): void { - echo "C.f4()\n"; - } -} - -function main() { - B::stest(); -} - -main(); -]]> - + And here is the output of the calls made in the static stest() function of class B. diff --git a/__docs/phpdoc/en/hack/modes.xml b/__docs/phpdoc/en/hack/modes.xml index 403fb163..bac28c15 100644 --- a/__docs/phpdoc/en/hack/modes.xml +++ b/__docs/phpdoc/en/hack/modes.xml @@ -12,35 +12,7 @@ Strict mode is just what it sounds like. The Hack type checker will catch every type error (see the // decl exception below). Any and all code written in strict mode must be correctly type annotated (unless // UNSAFE is used - see below). Another rule is that code written in strict mode cannot call into non-Hack code, and still pass the type checker. Here is how strict mode is declared. - -str = "Hello"; - } - - public function foo(int $x, int $y): int { - if ($x < $y) { - return 27; - } - return 34; - } - - public function bar(string $a, string $b): string { - return $a . $b; - } -} -]]> - - + @@ -69,47 +41,13 @@ class StrictClass { Partial mode is the default of Hack. In partial mode, the type checker checks all types other than that encompassed by an // UNSAFE comment. Partial mode also allows for the partially typing of a class, method or function (e.g., only type a subset of its arguments). And, also unlike strict mode, partial mode allows engineers to call code that has not yet been "Hack-ified" (in other words, they can call into untyped code). - - $y) { - return 27; - } - return 34; -} - -// Not annotated. Not type checked. -function bar_partial($a, $b) { - return $a + $b; -} -]]> - + or, since partial is the default, the // partial can be omitted. In fact, omitting // partial is preferred. - - $y) { - return 27; - } - return 34; -} - -// Not annotated. Not type checked. -function bar_partial($a, $b) { - return $a + $b; -} -]]> - + @@ -126,33 +64,11 @@ function bar_partial($a, $b) { File1.php - - - + File2.php - - - + @@ -164,34 +80,8 @@ function foo_strict(bool $b): int { In practical terms, using // Decl is equivalent to putting // UNSAFE at the top of every function (actually, in this respect, this is really close to how the Hack type checker works). The following example demonstrates this equivalency: - - - - - - + + @@ -210,33 +100,8 @@ function bar(): string { Here are two examples that demonstrate how the coverage of // UNSAFE . - - $y) { - // UNSAFE - return "I am not checked by the type checker"; // Covered by UNSAFE - } - return 34; // NOT covered by UNSAFE -} -]]> - - - $y) { - return "I am not checked by the type checker"; // Covered by the UNSAFE - } - return true; // Covered by the UNSAFE -} -]]> - + + diff --git a/__docs/phpdoc/en/hack/nullable.xml b/__docs/phpdoc/en/hack/nullable.xml index fcb06b67..a57755f7 100644 --- a/__docs/phpdoc/en/hack/nullable.xml +++ b/__docs/phpdoc/en/hack/nullable.xml @@ -4,18 +4,7 @@ Hack introduces a safer way to deal with nulls through a concept known as the "Nullable" type. Nullable allows any type to have null assigned and checked on it. Nullable is very useful for primitive types that don't generally allow null as one of their values, such as bool and int, but it can also be useful for user-defined classes. Below is an example. Note the ? operand used to represent nullable. - - - + @@ -30,59 +19,17 @@ function check_not_null(?int $x): int { It is true that in PHP null can currently be assigned to any variable. For example: - - - + $x is assigned to null. And, of course, checks can be made programmatically to see if $x is null: - -x === null) { - echo "Null 1"; - } - - if (is_null($this->x)) { - echo "Null 2"; - } - } -} -]]> - + So what exactly is the Hack nullable feature trying to solve? In a statically-typed language such as C#, null cannot be assigned to a primitive like an int. One of Hack's primary purposes is to type check annotated code, bringing a statically-typed feel to the dynamic language that is PHP. Taking the above example and "hackifying" it, the Hack type checker will complain, as it should since null is not in the range of values for int. - -x === null) { - echo "Null 1"; - } - - if (is_null($this->x)) { - echo "Null 2"; - } - } -} -]]> - + &example.outputs; Nullables are useful when a possible invalid state for a primitive (value) type is required. For example, a bool could be a common scenario where true or false has possibly not been determined yet. Here is an example: - -boolMayBeNull(true); - $y = $nb->boolMayBeNull(false); - $z = $nb->boolMayBeNull(null); - var_dump($x); - var_dump($y); - var_dump($z); -} - -main_nt(); -]]> - + &example.outputs; It may seem like using nullable should just be the default for primitives, just in case there is some sort of null state. However, many times it is reasonable to guarantee that a variable will be initialized, and, if somehow the variable is null, a serious problem has occurred. For example: - -count(); - } -} - -function main_nt() { - $nna = new NullableNotAppropriate(); - $y = $nna->noNullableNeeded(); - var_dump($y); -} - -main_nt(); -]]> - + There is certainly nothing technically wrong with the above code. ?int can be used as the return type annotation and Hack (nor HHVM) will not complain. However, the ?int is too relaxed. While this example is contrived, there is no way that $vec->count() will ever be null. And if it ever is, then something is wrong with the Vector implementation or the runtime. @@ -201,31 +101,7 @@ main_nt(); Here is a quick example that type checks correctly in Hack and produces valid output with HHVM: - - 5) { - return 5; - } else { - return null; - } - } -} - -function main_nt() { - $nt = new NullableTest(); - $y = $nt->mayReturnNull(10); - var_dump($y); - $y = $nt->mayReturnNull(4); - var_dump($y); -} - -main_nt(); -]]> - + &example.outputs; Without nullable, the Hack type checker would balk. For example, if ? was left out of the annotated return type, Hack would give an invalid return type error. Here is the incorrect code and the Hack error output: - - 5) { - return 5; - } else { - return null; - } - } -} - -function main_nt() { - $nt = new NullableTest(); - $y = $nt->mayReturnNull(10); - var_dump($y); - $y = $nt->mayReturnNull(4); - var_dump($y); -} - -main_nt(); -]]> - + &example.outputs; Nullable works with XHP elements as well. - - 5) { - return
Hello World
; - } else { - return null; - } - } -} - -function main_nt() { - $nt = new NullableTest(); - $y = $nt->mayReturnNull(10); - var_dump($y); - $y = $nt->mayReturnNull(4); - var_dump($y); -} - -main_nt(); -]]> -
+
@@ -320,43 +150,7 @@ main_nt(); Here is the example above enhanced with a new function that takes a nullable as a parameter: - - 5) { - return 5; - } else { - return null; - } - } - - public function nullableParameter(?int $x): int { - if (is_null($x)) { - return 100; - } else { - return -1; - } - } -} - -function main_nt() { - $nt = new NullableTest(); - $y = $nt->mayReturnNull(10); - var_dump($y); - $y = $nt->mayReturnNull(4); - var_dump($y); - - $z = $nt->nullableParameter(10); - var_dump($z); - $z = $nt->nullableParameter(null); - var_dump($z); -} - -main_nt(); -]]> - + &example.outputs; Now, if the nullable is taken away from the parameter in nullableParameter(), Hack will balk. - - 5) { - return 5; - } else { - return null; - } - } - - public function nullableParameter(int $x): int { - if (is_null($x)) { - return 100; - } else { - return -1; - } - } -} - -function main_nt() { - $nt = new NullableTest(); - $y = $nt->mayReturnNull(10); - var_dump($y); - $y = $nt->mayReturnNull(4); - var_dump($y); - - $z = $nt->nullableParameter(10); - var_dump($z); - $z = $nt->nullableParameter(null); - var_dump($z); -} - -main_nt(); -]]> - + &example.outputs; The reason that HHVM throws an error is due to the scalar type annotating capability on function parameters that is built into the runtime. An interesting "fix" to this particular issue is to provide null as the default value to the parameter $x. However, doing this will very appropriately cause Hack to balk.
- - 5) { - return 5; - } - else { - return null; - } - } - - public function nullableParameter(int $x = null): int { - if (is_null($x)) { - return 100; - } - else { - return -1; - } - } -} - -function main_nt() { - $nt = new NullableTest(); - $y = $nt->mayReturnNull(10); - var_dump($y); - $y = $nt->mayReturnNull(4); - var_dump($y); - - $z = $nt->nullableParameter(10); - var_dump($z); - $z = $nt->nullableParameter(null); - var_dump($z); -} - -main_nt(); -]]> - + &example.outputs; It is worth noting that nullable can be used on user-defined types. Take this example: - -userDefinedTypeMayBeNull(null); - $y = $nb->userDefinedTypeMayBeNull(new UserDefinedType()); - var_dump($x); - var_dump($y); -} - -main_nt(); -]]> - + &example.outputs; Caller - - - + Callee - - - + Hack complained about this code with the following message (line numbers may not be accurate): @@ -608,40 +270,10 @@ HipHop Fatal error: Argument 2 passed to create_updateA() must be an instance of The $comments parameter in create_updateA() has been type annotated with a string. However, the caller function updateA() had set the string to be passed to create_updateA() as null. There were two ways to solve this problem. The good way and the bad way. The "good" way to solve this problem (and the one that was actually implemented and accepted at this company) is to use nullable. - - - + The above solution solves both the Hack and HHVM complaints that was seen without nullable. The "bad" way would solve the HHVM problem, but not the Hack problem. Basically, provide $comments in create_updateA() a default value of null. - - - + Again, as shown in the example above on nullable method parameters, using a default value of null in this case is not type correct and should not be done under normal circumstances. @@ -653,29 +285,7 @@ function create_updateA(string $key, string $comments = null) { There is a converse argument for having a nullable type mechanism. Since Hack checks whether a type is annotated with the ability to return null, there are now cases where you won't have to do extraneous null checks in your code. - - 4) { - return false; - } - return true; -} - -function bar(): bool { - return foo(5); - // In the past, you might have had to do this check - //$x = foo(5); - //if ($x !== null) { - // return $x; - //} else { - // throw; - //} -} -]]> - + Before Hack, with code like this (granted this is an unrealistic example, but used to prove a point), you might check for null and throw an exception in bar() just in case foo() might give you a null back. But since the Hack type checker would produce an error if foo() tried to return null without being a nullable bool, you do not need that check. @@ -695,38 +305,7 @@ function bar(): bool { There are times where you have to deal with member variables in a special way when it is possible that they can be null. Take this example: - -x !== null) { - $a = $this->x->get(); - $b = $this->x->get(); - if ($b - $a < 1) { - return $this->x; - } - } - return new BizBang(); - } -} - -function main_nmv() { - $c = new NullMemVar(); - var_dump($c->foo()); -} - -main_nmv(); -]]> - + &example.outputs; Here is how to write the above code to make sure the Hack type checker is happy: - -x; - if ($local_for_x !== null) { - $a = $local_for_x->get(); - $b = $local_for_x->get(); - if ($b - $a < 1) { - return $local_for_x; - } - } - return new BizBang(); - } -} - -function main_nmv() { - $c = new NullMemVar(); - var_dump($c->foo()); -} - -main_nmv(); -]]> - + Notice how a local variable to foo() called $local_for_x is assigned to the class member variable. Now, it is provable that $local_for_x will not be set to null by get() since get() can have no effect on $local_for_x (i.e., $local_for_x is never on the RHS of an assignment statement). @@ -803,37 +350,7 @@ main_nmv(); Sometimes a nullable type needs to be passed to a method that takes the non-nullable variant of that same type. For example, assume the following piece of code and the output from the Hack type checker: - - 4) { - return $y + 5; - } else { - return null; - } - } -} - -class NullableNullThrowsTest { - protected int $x; - - public function __construct() { - $nnt = new NullableNullThrows(); - $nullable_value = $nnt->foo(2); - $this->x = $nullable_value; - } -} - -function main_nnt() { - $nntt = new NullableNullThrowsTest(); -} - -main_nnt(); -]]> - + &example.outputs; There are legitimate times when there is an expectation that a variable will not be null, and, if that variable is null, a runtime exception should be thrown. For these cases, use a library function such as nullthrows(). The nullthrows() definition is something like: - -(?T $x, ?string $message = null): T { - if ($x === null) { - throw new Exception($message ?: 'Unexpected null'); - } - - return $x; -} -]]> - + nullthrows() checks if the value associated with a type T is null. If the value is null, an exception is thrown. If not, the value is returned unmodified. An optional exception message can be supplied to nullthrows() as well. The code above can be rewritten to use nullthrows(), making the Hack type checker happy: - - 4) { - return $y + 5; - } else { - return null; - } - } -} - -class NullableNullThrowsTest { - protected int $x; - - public function __construct() { - $nnt = new NullableNullThrows(); - $nullable_value = $nnt->foo(2); - $this->x = nullthrows($nullable_value); - } -} - -function main_nnt() { - $nntt = new NullableNullThrowsTest(); -} - -main_nnt(); -]]> - + @@ -909,20 +385,7 @@ main_nnt(); Take this piece of code and associated Hack error message: - -fluffy = get_bunny(); - pet_bunny($this->fluffy); - } -} -]]> - + &example.outputs; - -fluffy = get_bunny(); - } - - public function pet(): void { - pet_bunny($this->fluffy); - } -} -]]> - - -fluffy; - if (!$fluffy) { - $fluffy = $this->fluffy = get_bunny(); - } - return $fluffy; - } - - public function pet(): void { - pet_bunny($this->getFluffy()); - } -} -]]> - + + @@ -1007,62 +432,9 @@ class PettingZoo { Take this scenario: File1.php - - - + File2.php - -i = $i; - var_dump($this); - } -} - -class IMVChild2 extends InitializeMemberVariables { - - protected string $s; - protected bool $b; - protected int $i; - - // Parent constructor called, setup called from there and dispatched to here - // to initialize member variables - protected function setup(string $s, bool $b, int $i) { - $this->s = $s; - $this->b = $b; - $this->i = $i; - var_dump($this); - } - -} - -$imvc = new IMVChild(4); -$imvc2 = new IMVChild2("Hi", true, 3); -]]> - + &example.outputs; Hack provides the capability to indicate that a function takes a variable number of arguments. This is done using ... (three dots). - - - + The general gotcha here will be that any explicitly defined parameters are counted in the call to func_get_args(). @@ -38,26 +18,7 @@ main_vna(); Here is another example: - - - + With the above example, HHVM will give an error (probably Invalid Operand Type) if anything "non-summable" (e.g., array()) is passed to sum() as a variable argument. @@ -78,14 +39,7 @@ main_vna(); In PHP, float and int are incompatible at the type hint level: - - - + &example.outputs; This incompatibility implies that the distinction between int and float must be tracked. However, some math operators have a return type which depends on their input: - - - + &example.outputs; - - - + @@ -216,46 +147,14 @@ function f2(int $x, float $y): int { Assume a nullable ?int variable and a function which takes an int. There are two choices for calling this function with a nullable. ?int can be cast to an int, in which case null will be converted to 0. Or code can be written like this: - - - + When is_int($x) is written inside a conditional statement, the Hack type checker will know that within the if/then branch, $x is an int. In the if/else branch, the type will be null. Here is another example: - -blah(); - ... - } else { - // $x is still just a Foo - ... - } -} -]]> - + @@ -267,71 +166,19 @@ function foo(Foo $x): void { This code is fine: - -doSomething(); - $y = $x * 2; - } - } - - public function doSomething(): void {} -} -]]> - + This code however is incorrect: - -x)) { - $this->doSomething(); - // can no longer assume $this->x is an int, doSomething might have changed it back to null. - // note: can't analyze doSomething() because a child class of Foo might change its behavior. - $y = $this->x * 2; - } - } - - public function doSomething(): void {} -} -]]> - + Here is a possible fix to the above problem: - -x; - if (is_int($x)) { - $this->doSomething(); - $y = $x * 2; - } - } - - public function doSomething(): void {} - } -]]> - + @@ -342,21 +189,7 @@ class Foo { Hack enforces strict rules regarding class initialization. The following piece of code is correct: - -x = 10; - } -} -]]> - + @@ -366,83 +199,30 @@ class B extends A { In the non-abstract case, the following piece of code is accepted: - - - + However, this piece of code is not accepted: - - - + Again, the type checker will complain about the "class member is not always properly initialized". The solution is to write the accepted case above or use __construct(): - -x = 10; - } -} -]]> - + There is another implication here. A protected or a public instance method cannot be called before the constructor has finished initializing the member variables. The following piece of code is correct: - -x = 10; - $this->foo(); - } - protected function foo() { - ... - } -} -]]> - + Finally, note that class initialization does not apply to nullable types. So, the following code is correct and acceptable: - - - + @@ -460,38 +240,7 @@ class Foo { Take the following example: - -testFun1(); - $f->testFun2(); -} - -main_cufff(); -]]> - + The above code shows the use of call_user_func() to call cufun1() from the function testFun1(). This code runs perfectly fine in HHVM. But, if <?hh was used instead of <?php, this code would not pass the Hack type checker: @@ -505,11 +254,7 @@ This call is invalid, this is not a function, it is a string Hack throws an error here because its type inference and safety cannot be guaranteed when using a string as the function name to call_user_func(). For example, imagine changing the call_user_func() line to be: - - - + Normally Hack would catch such a problem (passing an int to a function that takes a string), but Hack cannot provide these guarantees with call_user_func(). Thus an HHVM fatal error will be thrown *at runtime*. @@ -525,47 +270,11 @@ HipHop Fatal error: Argument 1 passed to cufun1() must be an instance of string, In order to make these type of callbacks type checkable and type-safe, Hack has introduced fun. fun is a special function used to create a "pointer" to a function in a type-safe way. fun takes a string corresponding to the name of the function to be called. It returns a "type-safe" string that can be used in, for example, call_user_func(). Building upon the example above to use fun: - -testFun1(); - $f->testFun2(); -} - -main_fff(); -]]> - + Before calling call_user_func(), fun is called. Returned from fun is a string() that can be used and analyzed by the Hack type checker. This code runs exactly the same in HHVM as the first example, but, now the Hack type checker can catch typing errors. In testFun1(), imagine passing an int instead of a string. Hack will throw an error. - - - + &example.outputs; It is important to note that the argument to fun must be a single-quoted, constant literal string representing a valid function name. For example: - - - + &example.outputs; Hack provides the ability to annotate return types on functions. This brings with it another added feature. Under a specific circumstance, Hack provides the ability to override functions based on just return type. That circumstance is a subclass overriding a method in a parent class with a return type compatible with the return type of the parent class. For example: - - - + @@ -651,43 +325,13 @@ class BB extends AA { Here are some unsupported examples: - - - + In the above, Hack will balk since int and Foo are not compatible types. - - - + In the above, Hack will again balk since returning Foo from BB:bar() is not compatible with AA:bar() returning FooChild. The other way around, as shown above, does, of course, make Hack happy. @@ -697,15 +341,7 @@ class BB extends AA { Overloading on return type within the same class is not supported by Hack (in fact, like PHP, no overloading exists at all; thus, this holds for function arguments as well). For example: - - - +
@@ -716,44 +352,7 @@ class AA { There are times when it is desirable to have an object be type checked as a more specific type than it is currently declared. For example, an interface needs to be type checked as one of its implementing classes. invariant() is used to help the Hack type checker make this more specific type determination. - -yay!"; - } -} - -function baz(int $a): I { - return $a === 1 ? new A() : new B(); -} - -function bar(): B { - $iface = baz(2); - invariant($iface instanceof B, "must be a B"); - $iface->yay(); - return $iface; -} - -bar(); -]]> - + Without the invariant(), Hack will give an error similar to the following: @@ -771,35 +370,7 @@ Check this out Here is another example where a variable can be mixed and invariant is used to help Hack understand $untyped_arrayis a typed array. - -foo_method(); // Hack now understands that $untyped_array is an array - return true; -} - -bar(); -]]> - + Without the invariant(), the Hack type checker would give an error similar to the following: &example.outputs; @@ -824,22 +395,7 @@ This is not a container, this is a mixed value The Hack type checker will throw an error when trying to call non-private methods during the initialization of an object. Here is an example: - -doOtherInit(); - $this->fluffy = new FluffyBunny(); - } - - protected function doOtherInit(): void { } -} -]]> - + &example.outputs; fluffy can still potentially be nu Here is an example of the first fix: - -doOtherInit(); - $this->fluffy = new FluffyBunny(); - } - - private function doOtherInit(): void { } -} -]]> - + @@ -897,41 +438,7 @@ class PettingZoo { Hack does not support union types. The following piece of code will run in HHVM. However it will not pass the Hack type checker, even though many would believe that it should: - -utBar(); - } - $a->utFoo(); -} - -function main_ut() { - $b = new UTB(); - ut_xyz($b); -} - -main_ut(); -]]> - + &example.outputs; The scenario describes above represents union types. Union types allow a variable to possibly have several, distinct representations. For performance reasons, Hack does not support this paradigm. To alleviate the type error presented above, local variables can be used. - -utBar(); - } - $a->utFoo(); -} - -function main_ut() { - $b = new UTB(); - ut_xyz($b); -} - -main_ut(); -]]> - + The local variable $a_local is assigned to $a. Now since these two variables are distinct, each can represent a different type. Hack will not throw a type error in this case. @@ -1008,18 +480,7 @@ main_ut(); Hack supports the type checking of the PHP string building technique called heredocs (e.g., $x = <<<EOF ..... EOF;). Hack does not balk when encountering a heredoc within PHP code, and throws an error when using heredocs incorrectly. Here is an example of Hack catching a heredoc error: - - - + &example.outputs; Of course, to fix this error, one needs to terminate the heredoc: - - - + There is a known current limitation with Hack's support of heredocs. The following code will not parse: - - - + &example.outputs; Hack supports nowdocs. In summary, nowdocs are similar to heredocs but without the parsing. Here is an example: - -bar[1]}. -This should not print a capital 'A': \x41 -EOT; -} - -foo(); -]]> - + &example.outputs; Example with ReflectionClass - - - + Example with Doctrine - -find(Entity\User::class, 5); -// instead of -$user = $entityManager->find('MyVendor\SomeComponent\TargetEntityNs\User', 5); -]]> - + diff --git a/__docs/phpdoc/en/hack/predefined/arrayaccesstktv.xml b/__docs/phpdoc/en/hack/predefined/arrayaccesstktv.xml index 4217a9e2..28c7f33f 100644 --- a/__docs/phpdoc/en/hack/predefined/arrayaccesstktv.xml +++ b/__docs/phpdoc/en/hack/predefined/arrayaccesstktv.xml @@ -23,24 +23,7 @@ For example, the following code should be expected to type check and run correctly on HHVM. It does type check with no errors, but you will get a fatal error in HHVM. - - { - public function offsetExists(string $offset): bool { return true;} - public function offsetGet(string $offset): Bar { return new Bar();} - public function offsetSet(string $offset, Bar $value): this { return $this;} - public function offsetUnset(string $offset): this { return $this;} -} - -function main(): void { - $x = new Foo(); - $x->offsetSet("Hi", new Bar()); -} -]]> - + &example.outputs; Currently HHVM is too strict when matching interface methods. Right now, for example, offsetExists(string) is not compatible with offsetExists(Tk). So if we change the code to remedy the HHVM problem, we will get a Hack type checker issue. - - implements ArrayAccess { - public function offsetExists(Tk $offset): bool { return true;} - public function offsetGet(Tk $offset): Tv { return new Bar();} - public function offsetSet(Tk $offset, Tv $value): this { return $this;} - public function offsetUnset(Tk $offset): this { return $this;} -} - -function main(): void { - $x = new Foo(); - $x->offsetSet("Hi", new Bar()); -} -]]> - + &example.outputs; In order to solve this problem (for now), just remove the generic type annotations (i.e. Tk and Tv from the method parameters and returns in the class that implement ArrayAccess. The following code will both type check (in non // strict mode) and run correctly. - - implements ArrayAccess { - public function offsetExists($offset): bool { return true;} - public function offsetGet($offset) { return new Bar();} - public function offsetSet($offset, $value): this { return $this;} - public function offsetUnset($offset): this { return $this;} -} - -function main(): void { - $x = new Foo(); - $x->offsetSet("Hi", new Bar()); -} -]]> - + @@ -111,19 +60,7 @@ function main(): void { The Hack type checker will allow for objects to implement the ArrayAccess (e.g., the type checker will make sure the interface is implemented correctly), but the type checker does not support using array semantics on such an object.... and there are no plans to allow this behavior. For example: - - - -function main(): void { - $o = new Obj(); - $o->offsetSet(3, 3); // works fine - $o[4] = 4; // NOT SUPPORTED BY HACK -} -]]> - - + &example.outputs; Basic usage - - implements ArrayAccess { - private Map $container; - public function __construct() { - $this->container = Map {}; - } - public function offsetSet(Tk $key, Tv $value): this { - if (!is_null($key)) { - $this->container[$key] = $value; - } - return $this; - } - public function offsetExists(Tk $offset): bool { - return isset($this->container[$offset]); - } - public function offsetUnset(Tk $offset): this { - unset($this->container[$offset]); - return $this; - } - public function offsetGet(Tk $offset): ?Tv { - return isset($this->container[$offset]) ? $this->container[$offset] : $ - } -} - -function main(): void { - $o = new Obj(); - $o->offsetSet(3, 3); - var_dump($o); -} - -main(); -]]> - + &example.outputs.similar; <function>ArrayAccess::offsetExists</function> example - - implements ArrayAccess { - public function offsetSet(Tk $offset, Tv $value): this { - var_dump(__METHOD__); - } - public function offsetExists(Tk $var): bool { - var_dump(__METHOD__); - if ($var == "foobar") { - return true; - } - return false; - } - public function offsetUnset(Tk $var): this { - var_dump(__METHOD__); - } - public function offsetGet(Tk $var): Tv { - var_dump(__METHOD__); - return "value"; - } -} - -$obj = new Obj(); - -echo "Runs obj::offsetExists()\n"; -var_dump(isset($obj["foobar"])); - -echo "\nRuns obj::offsetExists() and obj::offsetGet()\n"; -var_dump(empty($obj["foobar"])); - -echo "\nRuns obj::offsetExists(), *not* obj:offsetGet() as there is nothing to get\n"; -var_dump(empty($obj["foobaz"])); -?> -]]> - + &example.outputs.similar; key parameter will be set to &null; if another value is not available, like in the following example. - - -]]> - + &example.outputs; Basic usage - - implements IteratorAggregate { - public string $property1 = "Public property one"; - public string $property2 = "Public property two"; - public string $property3 = "Public property three"; - - // Using constructor promotion - public function __construct(public string $property) { - } - - public function getIterator(): { - return new ArrayIterator($this); - } -} - -$obj = new myData("last property"); - -foreach($obj as $value) { - var_dump($value); - echo "\n"; -} -]]> - + &example.outputs.similar; - - implements Iterator { - private $position = 0; - private $array = array( - "firstelement", - "secondelement", - "lastelement", - ); - - public function __construct() { - $this->position = 0; - } - - function rewind(): void { - var_dump(__METHOD__); - $this->position = 0; - } - - function current(): Tv { - var_dump(__METHOD__); - return $this->array[$this->position]; - } - - function next(): void { - var_dump(__METHOD__); - ++$this->position; - } - - function valid(): bool { - var_dump(__METHOD__); - return isset($this->array[$this->position]); - } -} - -$it = new myIterator; - -foreach($it as $value) { - var_dump($value); - echo "\n"; -} -]]> - + &example.outputs.similar; This example demonstrates how to iterate over a keyed iterator using &foreach;. - - { - protected $it = 0; - - public function current() { - return $this->it; - } - - public function key() { - return chr(65 + ($this->it % 5)); - } - - public function next() { - $this->it++; - } - - public function rewind() { - $this->it = 0; - } - - public function valid() { - return $this->it < 10; - } -} - -$it = new DuplicateStrIterator; - -foreach($it as $key => $value) { - var_dump($key, $value); - echo "\n"; -} -]]> - + &example.outputs.similar; Does this style of PHP code look familiar? - - null, 'url' => null, 'count' => 0); - $my_struct['id'] = '573065673A34Y'; - $my_struct['url'] = 'http://facebook.com'; - - var_dump($my_struct); - $my_struct = why_shapes($my_struct); - var_dump($my_struct); -} - -main_why_shapes(); -]]> - + &example.outputs; Shapes have the following general syntax: - - , 'id2' => ); -function foo(MyShape $x): void {} -]]> - + or - - , 'id2' => ); -function foo(MyShape $x): void {} -]]> - + @@ -97,25 +58,7 @@ function foo(MyShape $x): void {} This example creates a shape that represents a point in the two-dimensional plane. - - int, 'y' => int); - -function dotProduct(Point2D $a, Point2D $b): int { - var_dump($a); - var_dump($b); - return $a['x'] * $b['x'] + $a['y'] * $b['y']; -} - -function main_sse(): void { - echo dotProduct(shape('x' => 3, 'y' => 3), shape('x' => 4, 'y' => 4)); -} - -main_sse(); -]]> - + &example.outputs; shapesAcrossFiles1.php - - string, 'url' => string, 'count' => int); - -function foo_saf(RandomData $rd): RandomData { - if ($rd['id'] === '573065673A34Z') { - $rd['count']++; - } - else { - $rd['url'] = "http://google.com"; - $rd['count']--; - } - return $rd; -} -]]> - + shapesAcrossFiles2.php - - null, 'url' => null, 'count' => 0); - $rd['id'] = '573065673A34Y'; - $rd['url'] = 'http://facebook.com'; - var_dump($rd); - var_dump(foo_saf($rd)); - - - // This should cause a Hack error - // But this will still run in HHVM - $rd['count'] = 'I should error'; - var_dump(foo_saf($rd)); -} -]]> - + shapesAcrossFiles3.php - - - + Running main_saf() should produce a Hack error in setup_saf() since $rd['count'] was set to a string and the shape RandomData expects count to be an int when foo() is called. @@ -240,33 +135,7 @@ array(3) { Shapes can be used with generics as shown here: - - = shape ('x' => T, 'y' => T); - -function gen_shape_add(Point $pt1, Point $pt2): Point { - $sumx = $pt1['x'] + $pt2['x']; - $sumy = $pt1['y'] + $pt2['y']; - return shape ('x' => $sumx, 'y' => $sumy); -} - -function main_gs() { - // Float based shape - $pt1 = shape('x' => 1.0, 'y' => 2.0); - $pt2 = shape('x' => 3.0, 'y' => 4.0); - var_dump(gen_shape_add($pt1, $pt2)); - - // Int based shape - $pt3 = shape('x' => 1, 'y' => 2); - $pt4 = shape('x' => 3, 'y' => 4); - var_dump(gen_shape_add($pt3, $pt4)); -} - -main_gs(); -]]> - + &example.outputs; Hack will correctly type check all the code within a trait: - -x; - } - - public function getBin(Vector $vec): string { - return $vec[0]; - } -} -]]> - + &example.outputs; Hack will also throw an error on the following code, although the code runs perfectly fine. - -bar(); - } -} - -class AAA { - protected function bar(): int { - return 5; - } -} - -class BBB extends AAA { - use TTT; - - public function baz(): int { - return $this->foo(); - } -} - -function main_t(): void { - $bbb = new BBB(); - var_dump($bbb->baz()); -} - -main_t(); -]]> - + &example.outputs; Using $this in a trait on a method defined in the class where the trait will be used is not supported in Hack (remember traits are basically standalone entities to Hack). In order to remedy the above limitation from the Hack type checker, // UNSAFE could be used within foo(), or the file could be put in Hack's // decl mode. However, a better option is to use an abstract method within the trait. Traits support the use of abstract methods in order to impose requirements upon the class(es) where the trait will be used. - -bar(); - } -} - -class AAA { - protected function bar(): int { - return 5; - } -} - -class BBB extends AAA { - use TTT; - - public function baz(): int { - return $this->foo(); - } -} - -function main_t(): void { - $bbb = new BBB(); - var_dump($bbb->baz()); -} - -main_t(); -]]> - + &example.outputs; It is important to note that HHVM allows traits to implement interfaces (something not currently available in PHP), and Hack does support that feature with traits. The following example shows a trait implementing an interface. - - $vec): int; - public function get(): int; -} - -trait Foo implements Bar { - private int $x = 5; - - private function getVal(): int { - return $this->x; - } - - public function get(): int { - return $this->getVal(); - } - - public function babs(Vector $vec): int { - if (count($vec) > 0) { - return $vec[0]; - } - return -1; - } -} - -class Baz implements Bar { - use Foo; - - private Vector $v; - - public function __construct() { - $this->v = Vector {}; - $this->v[] = $this->get(); - } - - public function sass(): int { - return $this->babs($this->v); - } -} - -function main_traits() { - $b = new Baz(); - var_dump($b->sass()); -} - -main_traits(); -]]> - + &example.outputs; The Hack type checker is effective because it is able to take annotated facts sprinkled across a codebase (function entry and exit points, class definitions, etc), reconcile them, and detect any mismatches. Traits are a sizeable wrench in the works. The nature of traits in PHP is to "declare a bunch of stuff to be thrown into another scope at a later time, then check if it works at runtime"; the Hack type checker is only happy if it can take a piece of code and statically verify it independently of its usage sites. A syntactic way of having traits state that they only apply inside a particular type hierarchy allows their declarations to be both more explicit and more amenable to static analysis. - - - + When enforcing trait requirements, all T cares about is that X instanceof C is true and X instanceof I is true for any using class X. It doesn't care about how exactly compliance with X's declared interfaces is achieved, or how many layers of inheritance there might be between X and C. - - - + diff --git a/__docs/phpdoc/en/hack/tuples.xml b/__docs/phpdoc/en/hack/tuples.xml index 4b699404..0e44d8d7 100644 --- a/__docs/phpdoc/en/hack/tuples.xml +++ b/__docs/phpdoc/en/hack/tuples.xml @@ -15,37 +15,7 @@ Here is an example of using a tuple: - -test(); -} - -main_tup(); -]]> - + In fact, the Hack type checker will give you the something similar to the following error when trying to insert a new element into a tuple. @@ -98,15 +68,7 @@ Invalid index The discussion of tuples started with the premise that tuples are immutable. Well, that is true as far as Hack is concerned. In HHVM, tuples are arrays under the covers; thus, HHVM allows tuples to be mutated. In fact, tuples are currently a pure PHP implementation. - - - + func_get_args() returns an array comprising the a function's argument list. @@ -115,30 +77,7 @@ function tuple(...) { Repeating the code example from the introduction: - - - + &example.outputs; To review, here is how a tuple is created and used: - - - + However, when adding type annotations that require a tuple, the tuple reserved word is not used. - - { - $vec = Vector{$tup}; - return $vec; -} -]]> - + @@ -266,44 +187,7 @@ function test_tup((string, string) $tup): Vector<(string, string)> { There may be times when it is necessary to multiple values from a method. There are a few options available to accomplish this task. Arrays can be used. Some sort of collection type (e.g. Vector) could be used. And, of course, tuples could be used. Here is code that makes use of some of the options: - - { - $arr = array("Hello", 3); - $arr[2] = 4; - var_dump($arr); - return $arr; - } - - public function bar(): Vector { - $vec = Vector {"Hello", 3}; - $vec->add(4); - var_dump($vec); - return $vec; - } - - // This is how a tuple is returned from a method - public function baz(): (string, int) { - $tup = tuple("Hello", 3); - //$tup[2] = 4; - return $tup; - } -} - -function main_tup() { - $rmv = new ReturnMultipleValues(); - $rmv->foo(); - $rmv->bar(); - $rmv->baz(); -} - -main_tup(); -]]> - + So, why use a tuple when one could use an array()or Vector? The answer is mutability. Tuples by their very nature are immutable. No adding elements. No removing elements. No changing the types of values within the tuple (the values themselves can be changed, as long as they are type compatible). And having an immutable-style return type may be exactly was is specified. As discussed tuples are currently mutable at runtime. However, the Hack type checker will catch the mutation of tuples before runtime. If the code $tup[2] = 4; is uncommented, Hack will give the following error: @@ -323,15 +207,7 @@ Invalid index Tuples can be used in initializer expressions. For example, in the below example, class C has two tuple properties being initialized, one static and one instance: - - - + diff --git a/__docs/phpdoc/en/hack/typealiasing.xml b/__docs/phpdoc/en/hack/typealiasing.xml index a8fe986b..911b6d5f 100644 --- a/__docs/phpdoc/en/hack/typealiasing.xml +++ b/__docs/phpdoc/en/hack/typealiasing.xml @@ -8,45 +8,21 @@ Hack and HHVM are offering two ways to redefine type names: type aliasing and opaque type aliasing. Here is the syntax for each. Type Aliasing - - - + Opaque Type Aliasing - - - + Type aliases are declared outside of classes at the top-level. Also, the grammar on the RHS of the declaration is the same as would be put for type annotations on a parameter or return type. For example, take this function signature: - - - + A type alias that would replace (int, int) in foo() would look like: - - - + @@ -59,29 +35,9 @@ function foo(Point $x): void {} Type aliasing allows the redefining of an existing type name, but still refers to the existing type's underlying implementation. For example: File1.php - - - + File2.php - - - + &example.outputs; File1.php - - - + File2.php - - - + The file where SecretID is defined allows int operations such as add and subtract to be performed. However, in any other file Hack will throw an error when trying to use int operations on something declared as a SecretID @@ -161,45 +83,9 @@ It is incompatible with an object of type SecretID Here is another example where an opaque type alias is created from a string. The concatenation operator (.) can be used in the file where the opaque type alias is defined, but not in other files. File1.php - - - + File2.php - - - + &example.outputs; ot1.php - - - + ot2.php - - - + &example.outputs; The following syntax is not yet supported by the Hack type checker... - - as T = T; -]]> - + @@ -313,42 +158,12 @@ newtype Foo as T = T; Here is an example of using a opaque type aliases that defines the representation of a point on the (x,y) plane. File1.php - - - + Here is the test code: Test.php - - - + Since a opaque type alias was used, the underlying implementation of Point (i.e., tuple) cannot be accessed outside of the file it was defined. Thus, it is important that mechanisms are added to be able to construct a representation of the opaque type. In this case, createPoint() was defined to be able to do this construction. This is the HHVM output from running the above test code: @@ -367,50 +182,9 @@ main_tap(); Here is an example where string encodings are being type aliased. - - - - - - - - - + + + &example.outputs; This example demonstrates how opaque type aliases can help reduce conversion errors. Taking a class that deals with seconds, minutes and hours, the "old" way of creating such a class may have been: - - - + While a careful reading and understanding of the code is pretty clear what values should be passed to each method, and the developer may be able to create some internal checks to rule out bad values, anything can be passed to these functions. They are just ints after all. Minutes may be passed to funcForSeconds(), for example. Now look at the same class implemented using opaque type aliases: File1.php - - - + Any file outside of the one where the opaque type alias is declared and defined will not be able use the underlying representation to pass in errant values to the methods. Thus, to the outside world, Seconds are actually seconds and not ints. Here is test code for the above that should produce a type error: Test.php - -s = 464; - $m = TypeDefsConv::convertSecondsToMinutes($this->s); - echo $m; - TypeDefsConv::funcForMinutes($m); - } -} - -function main_tdc(): void { - $utdc = new UseTypeDefsConv(); - $utdc->foo(); -} - -main_tdc(); -]]> - + &example.outputs; Vector<Vector<T>>. That looks like a matrix. And it makes perfect sense to redefine the name of Vector<Vector<T>> as a Matrix<T>. File1.php - - = Vector>; - -function bar_ta_gen(Matrix $x): void { - var_dump($x); -} -]]> - + File2.php - -> $x): void { - bar_ta_gen($x); // Vector> is identical to Matrix -} - -function foo_ta_gen_main(): void { - $vecvec = Vector {Vector {1.0, 2.0}, Vector {3.0, 4.0}}; - foo_ta_gen($vecvec); -} - -foo_ta_gen_main(); -]]> - + @@ -562,36 +247,7 @@ foo_ta_gen_main(); Take this piece of code that includes a type alias: - - = string; - -class FooPhantom {} -function serialize_phantom(T $t): Serialized { - return serialize($t); -} -function unserialize_phantom(Serialized $s): T { - return unserialize($s); -} - -// Using the api: -function main_phantom(): void { - $x = new FooPhantom(); - var_dump($x); - // $serialized is a Serialized, aka "string" - $serialized = serialize_phantom($x); - var_dump($serialized); - // we now know the type of $y must be Foo - $y = unserialize_phantom($serialized); - var_dump($y); -} - -main_phantom(); -]]> - + &example.outputs; File1.php - - { - const Color BLUE = 1; - const Color RED = 2; - const Color GREEN = 3; - - public static function getColor(Color $color) { - switch ($color) { - case 1: return "0000ff"; - case 2: return "ff0000"; - case 3: return "00ff00"; - } -} -]]> - + File1.php - - - This is really a silly example. I hope you will not actually write code like this... - ; -} -]]> - + diff --git a/__docs/phpdoc/en/hack/unsupported.xml b/__docs/phpdoc/en/hack/unsupported.xml index f868dfb4..a14f5235 100644 --- a/__docs/phpdoc/en/hack/unsupported.xml +++ b/__docs/phpdoc/en/hack/unsupported.xml @@ -96,19 +96,7 @@ There is something else to note about what Hack doesn't support: top-level code. This may be surprising, but the Hack type checker wants everything in a class or function (global scope static analysis can prove to be very difficult). The implication here is that the Hack type checker will ignore any top-level code when in partial or decl modes, and actually throw an error in strict mode. For example: - - - + &example.outputs; Unlike PHP, Hack does not allow one to define a function on a class that collides with the class name. For example, this is acceptable in PHP: - - - + However, this is not acceptable in Hack: - - - + &example.outputs; The reason for Hack disallowing this comes down to constructors. In the above example, foo() behaves like a constructor and gets called upon instantiation using new Foo(). This could be very confusing especially when there is an explicit constructor as well. - - - + @@ -191,25 +156,11 @@ class Foo { Unlike PHP, Hack does not allow naming a constructor explicitly (e.g., by the name of the class): - - - + However, this is not acceptable in Hack: - - - + &example.outputs; PHP allows children to call static methods in the parent. For example: - - - + Hack does not currently allow this. The following error will be displayed: @@ -264,50 +200,13 @@ The closest thing is staticMethod but it's a static method In PHP, static methods can be called at the class level or the instance level. For example, the following two ways to call bar() are both acceptable: - - - + Hack does not allow the instance level static calls in // strict mode (this is allowed in // partial mode). Hack will balk about using "dynamic classes". - - - + &example.outputs; The purpose of XHPChild is to provide a concrete type for the {} operator. In the following XHP fragment: - - - + $foo is an XHPChild object. @@ -51,38 +47,19 @@ The XHP method appendChild() also takes an XHPChild. Thus, given the example XHP fragment above, the following code can be written as an equivalent alternative: - - -$div->appendChild($foo); -]]> - + When writing an API that takes an argument flowing into the {} operator, use the XHPChild type hint. - -{$msg}; -} -]]> - + Of course, there will be cases when a specific type other than XHPChild needs to be used, and that is obviously ok. - -getCurrentCount() -} -]]> - + @@ -102,19 +79,7 @@ function do_some_alignment(:ui:slideshow $x) { Returning XHPChild is useful in cases such as: - - 4) { - return "some string"; - } else { - $div =
; - return $div; // XHPChild - } -} -]]> - + @@ -160,15 +125,7 @@ function foo(int $x): mixed { XHP attributes have types. However, because they are written or read via setAttribute/getAttribute, which inherently cannot be typed except for mixed, it is difficult to type check attributes without special integration. A workaround is to integrate Hack with XHP such that attribute type checking occurs at call sites of those methods, or at __construct() call sites (i.e. native XHP syntax). In other words, the type checker could complain about the following intelligently instead of attempting to cast values or throwing a fatal exception/error at runtime: - -; -$div->setAttribute('class', 1); // Type error -$div->getAttribute('class')->someMethod(); // Type error -$num = ; // Type error -]]> - + diff --git a/__docs/phpdoc/en/hackref/collections/examples.xml b/__docs/phpdoc/en/hackref/collections/examples.xml index 0f696649..13d329e9 100644 --- a/__docs/phpdoc/en/hackref/collections/examples.xml +++ b/__docs/phpdoc/en/hackref/collections/examples.xml @@ -8,15 +8,7 @@ {extname} Example - - -]]> - + &example.outputs.similar; Construct it with a Traversable: - - 1, 'b' => 2); - $fm = new ImmMap($a); -} -]]> - + Or construct an ImmMap with literal syntax: - - 1, 'b' => 2}; -} -]]> - + diff --git a/__docs/phpdoc/en/hackref/collections/immmaptktv/construct.xml b/__docs/phpdoc/en/hackref/collections/immmaptktv/construct.xml index 70de8377..bb776479 100644 --- a/__docs/phpdoc/en/hackref/collections/immmaptktv/construct.xml +++ b/__docs/phpdoc/en/hackref/collections/immmaptktv/construct.xml @@ -29,19 +29,7 @@ Here is an example of constructing an ImmMap using an array - -1, "b"=>2, "c"=>3); - $y = new ImmMap($x); - var_dump($y); -} - -main(); -]]> - + &example.outputs; Here is an example of constructing an ImmSet using an array - - - + &example.outputs; Construction is as follows: - -toImmVector(); -]]> - - - - - - - + + + diff --git a/__docs/phpdoc/en/hackref/collections/immvectortv/construct.xml b/__docs/phpdoc/en/hackref/collections/immvectortv/construct.xml index 904f04a8..bb827b91 100644 --- a/__docs/phpdoc/en/hackref/collections/immvectortv/construct.xml +++ b/__docs/phpdoc/en/hackref/collections/immvectortv/construct.xml @@ -29,19 +29,7 @@ Here is an example of constructing an ImmVector using an array - - - + &example.outputs; Here is an example of constructing an Map using an array - -1, "b"=>2, "c"=>3); - $y = new Map($x); - var_dump($y); -} - -main(); -]]> - + &example.outputs; - - - + Pairs only support integer keys of 0 and 1. If a non-integer key is used, an exception will be thrown. diff --git a/__docs/phpdoc/en/hackref/collections/settv/construct.xml b/__docs/phpdoc/en/hackref/collections/settv/construct.xml index 235e7ead..8b06d57d 100644 --- a/__docs/phpdoc/en/hackref/collections/settv/construct.xml +++ b/__docs/phpdoc/en/hackref/collections/settv/construct.xml @@ -29,19 +29,7 @@ Here is an example of constructing an Set using an array - - - + &example.outputs; Here is an example of constructing an Vector using an array - - - + &example.outputs; Simple <function>class_meth</function> usage - - - + <function>class_meth</function> is most useful for callbacks - - - + <function>class_meth</function> used as callback for a filter - -filter(class_meth('C', 'isOdd')); // returns Vector { 1, 3 } -]]> - + diff --git a/__docs/phpdoc/en/hackref/hackmagic/fun.xml b/__docs/phpdoc/en/hackref/hackmagic/fun.xml index 9f1a0060..ae38671f 100644 --- a/__docs/phpdoc/en/hackref/hackmagic/fun.xml +++ b/__docs/phpdoc/en/hackref/hackmagic/fun.xml @@ -45,27 +45,7 @@ <function>fun</function> gives the type checker visibility into a callback and function call - - - + The non fun() usage will be type checked as follows <function>fun</function> is most useful for callbacks - - - + diff --git a/__docs/phpdoc/en/hackref/hackmagic/inst-meth.xml b/__docs/phpdoc/en/hackref/hackmagic/inst-meth.xml index 37c947d0..bac7ee07 100644 --- a/__docs/phpdoc/en/hackref/hackmagic/inst-meth.xml +++ b/__docs/phpdoc/en/hackref/hackmagic/inst-meth.xml @@ -57,31 +57,7 @@ Simple <function>inst_meth</function> usage - - - + &example.outputs; <function>inst_meth</function> is most useful for callbacks - - - + <function>inst_meth</function> used as callback for a filter - - $data): Vector { - $callback = inst_meth($this, 'isOdd'); - return $data->filter($callback); - } -} - -$c = new C(); -$v = Vector { 1, 2, 3 }; -$c->filter($v); // Returns Vector { 1, 3 } -]]> - + diff --git a/__docs/phpdoc/en/hackref/hackmagic/invariant-callback-register.xml b/__docs/phpdoc/en/hackref/hackmagic/invariant-callback-register.xml index 861ccfb2..542df021 100644 --- a/__docs/phpdoc/en/hackref/hackmagic/invariant-callback-register.xml +++ b/__docs/phpdoc/en/hackref/hackmagic/invariant-callback-register.xml @@ -51,20 +51,7 @@ Using <function>invariant_callback_register</function> to throw a custom exception - - - + diff --git a/__docs/phpdoc/en/hackref/hackmagic/invariant-violation.xml b/__docs/phpdoc/en/hackref/hackmagic/invariant-violation.xml index a0dd036a..608d51df 100644 --- a/__docs/phpdoc/en/hackref/hackmagic/invariant-violation.xml +++ b/__docs/phpdoc/en/hackref/hackmagic/invariant-violation.xml @@ -52,23 +52,7 @@ <function>invariant_violation</function> affects control flow analysis - - - + diff --git a/__docs/phpdoc/en/hackref/hackmagic/invariant.xml b/__docs/phpdoc/en/hackref/hackmagic/invariant.xml index c96c2057..0c92ec3c 100644 --- a/__docs/phpdoc/en/hackref/hackmagic/invariant.xml +++ b/__docs/phpdoc/en/hackref/hackmagic/invariant.xml @@ -58,53 +58,13 @@ <function>invariant</function> used to remove null - - - + <function>invariant</function> used to assert a specific class - -haveFun(); - - // But we need to use invariant if we want to use it as a FunHaver without a - // type error. - invariant($f instanceof FunHaver, 'Expected a FunHaver'); - $f->haveLotsOfFun(); -} -]]> - + diff --git a/__docs/phpdoc/en/hackref/hackmagic/meth-caller.xml b/__docs/phpdoc/en/hackref/hackmagic/meth-caller.xml index d50e348b..d0f50f54 100644 --- a/__docs/phpdoc/en/hackref/hackmagic/meth-caller.xml +++ b/__docs/phpdoc/en/hackref/hackmagic/meth-caller.xml @@ -57,65 +57,19 @@ Simple <function>meth_caller</function> usage - - - + More realistic callback usage - - - + <function>meth_caller</function> used as callback for a map - -map(meth_caller('Vector', 'count')); // returns Vector {3, 1} -]]> - + diff --git a/__docs/phpdoc/sample-code/async/coalesced-fetching.php.hh.expect b/__docs/phpdoc/sample-code/async/coalesced-fetching.php.hh.expect deleted file mode 100644 index c19fb384..00000000 --- a/__docs/phpdoc/sample-code/async/coalesced-fetching.php.hh.expect +++ /dev/null @@ -1 +0,0 @@ -No errors! diff --git a/__docs/phpdoc/sample-code/async/coalesced-fetching.php.hhvm.expect b/__docs/phpdoc/sample-code/async/coalesced-fetching.php.hhvm.expect deleted file mode 100644 index 0605f76d..00000000 --- a/__docs/phpdoc/sample-code/async/coalesced-fetching.php.hhvm.expect +++ /dev/null @@ -1,15 +0,0 @@ -Fetch 2 ids: -array(2) { - [1]=> - int(1) - [3]=> - int(9) -} -Got result for id 1: 1 -Got result for id 3: 9 -Fetch 1 ids: -array(1) { - [2]=> - int(4) -} -Got result for id 2: 4 diff --git a/__docs/phpdoc/sample-code/class.hack.arrayaccesstktv1.php b/__docs/phpdoc/sample-code/class.hack.arrayaccesstktv1.php new file mode 100644 index 00000000..4489f716 --- /dev/null +++ b/__docs/phpdoc/sample-code/class.hack.arrayaccesstktv1.php @@ -0,0 +1,14 @@ + { + public function offsetExists(string $offset): bool { return true;} + public function offsetGet(string $offset): Bar { return new Bar();} + public function offsetSet(string $offset, Bar $value): this { return $this;} + public function offsetUnset(string $offset): this { return $this;} +} + +function main(): void { + $x = new Foo(); + $x->offsetSet("Hi", new Bar()); +} diff --git a/__docs/phpdoc/sample-code/class.hack.arrayaccesstktv2.php b/__docs/phpdoc/sample-code/class.hack.arrayaccesstktv2.php new file mode 100644 index 00000000..cd87e3d5 --- /dev/null +++ b/__docs/phpdoc/sample-code/class.hack.arrayaccesstktv2.php @@ -0,0 +1,14 @@ + implements ArrayAccess { + public function offsetExists(Tk $offset): bool { return true;} + public function offsetGet(Tk $offset): Tv { return new Bar();} + public function offsetSet(Tk $offset, Tv $value): this { return $this;} + public function offsetUnset(Tk $offset): this { return $this;} +} + +function main(): void { + $x = new Foo(); + $x->offsetSet("Hi", new Bar()); +} diff --git a/__docs/phpdoc/sample-code/class.hack.arrayaccesstktv3.php b/__docs/phpdoc/sample-code/class.hack.arrayaccesstktv3.php new file mode 100644 index 00000000..2ad33ded --- /dev/null +++ b/__docs/phpdoc/sample-code/class.hack.arrayaccesstktv3.php @@ -0,0 +1,14 @@ + implements ArrayAccess { + public function offsetExists($offset): bool { return true;} + public function offsetGet($offset) { return new Bar();} + public function offsetSet($offset, $value): this { return $this;} + public function offsetUnset($offset): this { return $this;} +} + +function main(): void { + $x = new Foo(); + $x->offsetSet("Hi", new Bar()); +} diff --git a/__docs/phpdoc/sample-code/class.hack.arrayaccesstktv4.php b/__docs/phpdoc/sample-code/class.hack.arrayaccesstktv4.php new file mode 100644 index 00000000..657f3a62 --- /dev/null +++ b/__docs/phpdoc/sample-code/class.hack.arrayaccesstktv4.php @@ -0,0 +1,8 @@ + + +function main(): void { + $o = new Obj(); + $o->offsetSet(3, 3); // works fine + $o[4] = 4; // NOT SUPPORTED BY HACK +} diff --git a/__docs/phpdoc/sample-code/class.hack.arrayaccesstktv5.php b/__docs/phpdoc/sample-code/class.hack.arrayaccesstktv5.php new file mode 100644 index 00000000..3b68d410 --- /dev/null +++ b/__docs/phpdoc/sample-code/class.hack.arrayaccesstktv5.php @@ -0,0 +1,31 @@ + implements ArrayAccess { + private Map $container; + public function __construct() { + $this->container = Map {}; + } + public function offsetSet(Tk $key, Tv $value): this { + if (!is_null($key)) { + $this->container[$key] = $value; + } + return $this; + } + public function offsetExists(Tk $offset): bool { + return isset($this->container[$offset]); + } + public function offsetUnset(Tk $offset): this { + unset($this->container[$offset]); + return $this; + } + public function offsetGet(Tk $offset): ?Tv { + return isset($this->container[$offset]) ? $this->container[$offset] : $ + } +} + +function main(): void { + $o = new Obj(); + $o->offsetSet(3, 3); + var_dump($o); +} + +main(); diff --git a/__docs/phpdoc/sample-code/class.hack.immmaptktv1.php b/__docs/phpdoc/sample-code/class.hack.immmaptktv1.php new file mode 100644 index 00000000..0cea6320 --- /dev/null +++ b/__docs/phpdoc/sample-code/class.hack.immmaptktv1.php @@ -0,0 +1,5 @@ + 1, 'b' => 2); + $fm = new ImmMap($a); +} diff --git a/__docs/phpdoc/sample-code/class.hack.immmaptktv2.php b/__docs/phpdoc/sample-code/class.hack.immmaptktv2.php new file mode 100644 index 00000000..e21f02f9 --- /dev/null +++ b/__docs/phpdoc/sample-code/class.hack.immmaptktv2.php @@ -0,0 +1,4 @@ + 1, 'b' => 2}; +} diff --git a/__docs/phpdoc/sample-code/class.hack.immvectortv1.php b/__docs/phpdoc/sample-code/class.hack.immvectortv1.php new file mode 100644 index 00000000..3a32e04d --- /dev/null +++ b/__docs/phpdoc/sample-code/class.hack.immvectortv1.php @@ -0,0 +1,3 @@ +toImmVector(); diff --git a/__docs/phpdoc/sample-code/class.hack.immvectortv2.php b/__docs/phpdoc/sample-code/class.hack.immvectortv2.php new file mode 100644 index 00000000..a2206c11 --- /dev/null +++ b/__docs/phpdoc/sample-code/class.hack.immvectortv2.php @@ -0,0 +1,3 @@ + implements IteratorAggregate { + public string $property1 = "Public property one"; + public string $property2 = "Public property two"; + public string $property3 = "Public property three"; + + // Using constructor promotion + public function __construct(public string $property) { + } + + public function getIterator(): { + return new ArrayIterator($this); + } +} + +$obj = new myData("last property"); + +foreach($obj as $value) { + var_dump($value); + echo "\n"; +} diff --git a/__docs/phpdoc/sample-code/class.hack.iteratortv1.php b/__docs/phpdoc/sample-code/class.hack.iteratortv1.php new file mode 100644 index 00000000..1aaef0f1 --- /dev/null +++ b/__docs/phpdoc/sample-code/class.hack.iteratortv1.php @@ -0,0 +1,40 @@ + implements Iterator { + private $position = 0; + private $array = array( + "firstelement", + "secondelement", + "lastelement", + ); + + public function __construct() { + $this->position = 0; + } + + function rewind(): void { + var_dump(__METHOD__); + $this->position = 0; + } + + function current(): Tv { + var_dump(__METHOD__); + return $this->array[$this->position]; + } + + function next(): void { + var_dump(__METHOD__); + ++$this->position; + } + + function valid(): bool { + var_dump(__METHOD__); + return isset($this->array[$this->position]); + } +} + +$it = new myIterator; + +foreach($it as $value) { + var_dump($value); + echo "\n"; +} diff --git a/__docs/phpdoc/sample-code/class.hack.keyediteratortktv1.php b/__docs/phpdoc/sample-code/class.hack.keyediteratortktv1.php new file mode 100644 index 00000000..c13b842d --- /dev/null +++ b/__docs/phpdoc/sample-code/class.hack.keyediteratortktv1.php @@ -0,0 +1,31 @@ + { + protected $it = 0; + + public function current() { + return $this->it; + } + + public function key() { + return chr(65 + ($this->it % 5)); + } + + public function next() { + $this->it++; + } + + public function rewind() { + $this->it = 0; + } + + public function valid() { + return $this->it < 10; + } +} + +$it = new DuplicateStrIterator; + +foreach($it as $key => $value) { + var_dump($key, $value); + echo "\n"; +} diff --git a/__docs/phpdoc/sample-code/class.hack.pairtv1tv21.php b/__docs/phpdoc/sample-code/class.hack.pairtv1tv21.php new file mode 100644 index 00000000..00e914f6 --- /dev/null +++ b/__docs/phpdoc/sample-code/class.hack.pairtv1tv21.php @@ -0,0 +1,7 @@ + diff --git a/__docs/phpdoc/sample-code/function.hack.class_meth1.php b/__docs/phpdoc/sample-code/function.hack.class_meth1.php new file mode 100644 index 00000000..3ae755e4 --- /dev/null +++ b/__docs/phpdoc/sample-code/function.hack.class_meth1.php @@ -0,0 +1,18 @@ +filter(class_meth('C', 'isOdd')); // returns Vector { 1, 3 } diff --git a/__docs/phpdoc/sample-code/function.hack.fun1.php b/__docs/phpdoc/sample-code/function.hack.fun1.php new file mode 100644 index 00000000..a5087f66 --- /dev/null +++ b/__docs/phpdoc/sample-code/function.hack.fun1.php @@ -0,0 +1,17 @@ + $data): Vector { + $callback = inst_meth($this, 'isOdd'); + return $data->filter($callback); + } +} + +$c = new C(); +$v = Vector { 1, 2, 3 }; +$c->filter($v); // Returns Vector { 1, 3 } diff --git a/__docs/phpdoc/sample-code/function.hack.invariant1.php b/__docs/phpdoc/sample-code/function.hack.invariant1.php new file mode 100644 index 00000000..5f1ce123 --- /dev/null +++ b/__docs/phpdoc/sample-code/function.hack.invariant1.php @@ -0,0 +1,8 @@ +haveFun(); + + // But we need to use invariant if we want to use it as a FunHaver without a + // type error. + invariant($f instanceof FunHaver, 'Expected a FunHaver'); + $f->haveLotsOfFun(); +} diff --git a/__docs/phpdoc/sample-code/function.hack.invariant_callback_register1.php b/__docs/phpdoc/sample-code/function.hack.invariant_callback_register1.php new file mode 100644 index 00000000..f81f96dd --- /dev/null +++ b/__docs/phpdoc/sample-code/function.hack.invariant_callback_register1.php @@ -0,0 +1,10 @@ +map(meth_caller('Vector', 'count')); // returns Vector {3, 1} diff --git a/__docs/phpdoc/sample-code/hack.annotations1.php b/__docs/phpdoc/sample-code/hack.annotations1.php new file mode 100644 index 00000000..c65cd1fc --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.annotations1.php @@ -0,0 +1,11 @@ + $test = Vector {}; + // The return type is a tuple. Again, the "tuple" reserved + // word is not used. + public function bar(): (string, string) { + return $this->test[0]; + } + public function foo() { + // But to use an actual tuple, use the "tuple" reserved word + $this->test->add(tuple('hello', 'world')); + } +} diff --git a/__docs/phpdoc/sample-code/hack.annotations11.php b/__docs/phpdoc/sample-code/hack.annotations11.php new file mode 100644 index 00000000..f00a4277 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.annotations11.php @@ -0,0 +1,15 @@ + $arr; + private array $arr2; + + public function __construct() { + $this->arr = array(); + $this->arr2 = array(); + } + + public function bar(T $val): array { + return array($val); + } + + public function sort(array $a): array { + sort($a); + return $a; + } + + public function baz(FooFoo $val): array { + $this->arr[] = $val; + return $this->arr; + } +} + +function main_aa() { + $haa = new HackArrayAnnotations(); + var_dump($haa->bar(3)); + var_dump($haa->bar(new FooFoo())); + var_dump($haa->sort(array(1.3, 5.6, 2.3, 0.2, 1.4))); + var_dump($haa->baz(new FooFoo())); +} + +main_aa(); diff --git a/__docs/phpdoc/sample-code/hack.annotations15.php b/__docs/phpdoc/sample-code/hack.annotations15.php new file mode 100644 index 00000000..e63fb4b0 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.annotations15.php @@ -0,0 +1,22 @@ + $arr; + private int $i; + + public function __construct() { + $this->arr = array(new BarBar()); + $this->i = 4; + } + public function getBars(): array { + if ($this->i < 5) { + return array(); + } else if ($this->i < 10) { + return $this->arr; + } else { + return array(null); // Type Error + } + } +} diff --git a/__docs/phpdoc/sample-code/hack.annotations16.php b/__docs/phpdoc/sample-code/hack.annotations16.php new file mode 100644 index 00000000..8f44d69c --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.annotations16.php @@ -0,0 +1,19 @@ +x = $new_x; + // $this has type "this" + return $this; + } + public static function newInstance(): this { + // new static() has type "this" + return new static(); + } + public function newCopy(): this { + // This would not type check with self::, but static:: is ok + return static::newInstance(); + } + // You can also say Awaitable; + public async function genThis(): Awaitable { + return $this; + } +} + +final class Child { + public function newChild(): this { + // This is OK because Child is final. + // However, if Grandchild extends Child, then this would be wrong, since + // $grandchild->newChild() should returns a Child instead of a Grandchild + return new Child(); + } +} diff --git a/__docs/phpdoc/sample-code/hack.annotations2.php b/__docs/phpdoc/sample-code/hack.annotations2.php new file mode 100644 index 00000000..2dd35805 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.annotations2.php @@ -0,0 +1,11 @@ +newBase() + // should return a Child, but it always returns a Base! + return new Base(); + } + + public static function newBase2(): this { + // ERROR! This is wrong for the same reason that new Base() is wrong + return new self(); + } + + // This function is fine + abstract public static function goodNewInstance(): this; + + public static function badNewInstance(): this { + // ERROR! Child::badNewInstance() would call Base::goodNewInstance() which is wrong + return self::goodNewInstance(); + } +} diff --git a/__docs/phpdoc/sample-code/hack.annotations21.php b/__docs/phpdoc/sample-code/hack.annotations21.php new file mode 100644 index 00000000..5029d1e5 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.annotations21.php @@ -0,0 +1,11 @@ + { + yield 1; + yield 2; + yield 3; +} + +function foo(): void { + foreach (gen() as $x) { + echo $x, "\n"; + } +} diff --git a/__docs/phpdoc/sample-code/hack.annotations26.php b/__docs/phpdoc/sample-code/hack.annotations26.php new file mode 100644 index 00000000..699a5417 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.annotations26.php @@ -0,0 +1,10 @@ + { + return 42; +} + +async function g(): Awaitable { + $f = await f(); + $f++; + return 'hi test ' . $f; +} diff --git a/__docs/phpdoc/sample-code/hack.annotations3.php b/__docs/phpdoc/sample-code/hack.annotations3.php new file mode 100644 index 00000000..f0a15d6c --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.annotations3.php @@ -0,0 +1,14 @@ +t = $val; + $this->a = new FooClass(); + $this->ni = $val === 3 ? null : 4; + $this->x = function(int $n, int $m): string { + $r = ''; + for ($i=0; $i < $n+$m; $i++) { + $r .= "hi"; + } + return $r; + }; + } +} diff --git a/__docs/phpdoc/sample-code/hack.annotations5.php b/__docs/phpdoc/sample-code/hack.annotations5.php new file mode 100644 index 00000000..9af939a6 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.annotations5.php @@ -0,0 +1,31 @@ + $arr): int { + $s = 0; + foreach ($arr as $v) { + $s += $v; + } + return $s; +} diff --git a/__docs/phpdoc/sample-code/hack.arrayaccesstktv.offsetexists1.php b/__docs/phpdoc/sample-code/hack.arrayaccesstktv.offsetexists1.php new file mode 100644 index 00000000..13cfddaf --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.arrayaccesstktv.offsetexists1.php @@ -0,0 +1,32 @@ + implements ArrayAccess { + public function offsetSet(Tk $offset, Tv $value): this { + var_dump(__METHOD__); + } + public function offsetExists(Tk $var): bool { + var_dump(__METHOD__); + if ($var == "foobar") { + return true; + } + return false; + } + public function offsetUnset(Tk $var): this { + var_dump(__METHOD__); + } + public function offsetGet(Tk $var): Tv { + var_dump(__METHOD__); + return "value"; + } +} + +$obj = new Obj(); + +echo "Runs obj::offsetExists()\n"; +var_dump(isset($obj["foobar"])); + +echo "\nRuns obj::offsetExists() and obj::offsetGet()\n"; +var_dump(empty($obj["foobar"])); + +echo "\nRuns obj::offsetExists(), *not* obj:offsetGet() as there is nothing to get\n"; +var_dump(empty($obj["foobaz"])); +?> diff --git a/__docs/phpdoc/sample-code/hack.arrayaccesstktv.offsetset1.php b/__docs/phpdoc/sample-code/hack.arrayaccesstktv.offsetset1.php new file mode 100644 index 00000000..6b3bd982 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.arrayaccesstktv.offsetset1.php @@ -0,0 +1,5 @@ + diff --git a/__docs/phpdoc/sample-code/hack.arrays1.php b/__docs/phpdoc/sample-code/hack.arrays1.php new file mode 100644 index 00000000..6fb556c4 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.arrays1.php @@ -0,0 +1,28 @@ + { + private array $arr = array(); + private array $arr2 = array(); + private array $arr3 = array(); + + public function __construct(T $data) { + $this->arr2[0] = $data; + } + + public function bar(T $data): void { + $this->arr = array(); + var_dump($this->arr); + $this->arr2[] = $data; + var_dump($this->arr2); + $this->arr3["hi"] = new Foo(); + var_dump($this->arr3); + } +} + +function main_arr() { + $ha = new HackArrays("Facebook"); + $ha->bar("Food"); +} + +main_arr(); diff --git a/__docs/phpdoc/sample-code/async/coalesced-fetching.php b/__docs/phpdoc/sample-code/hack.async1.php similarity index 92% rename from __docs/phpdoc/sample-code/async/coalesced-fetching.php rename to __docs/phpdoc/sample-code/hack.async1.php index ea5fba1c..2c5ef100 100644 --- a/__docs/phpdoc/sample-code/async/coalesced-fetching.php +++ b/__docs/phpdoc/sample-code/hack.async1.php @@ -1,9 +1,7 @@ -> $pendingWH = null; private static array $ids = array(); - public static async function fetch(int $id): Awaitable { self::$ids[$id] = $id; if (self::$pendingWH === null) { @@ -12,41 +10,33 @@ class Batcher { $results = await self::$pendingWH; return $results[$id]; } - private static async function fetchDispatch(): Awaitable> { await RescheduleWaitHandle::create(0, 0); $ids = self::$ids; self::$ids = array(); self::$pendingWH = null; - // do expensive serial multi-fetch echo "Fetch ".count($ids)." ids:\n"; $results = array_map($id ==> $id * $id, $ids); var_dump($results); - return $results; } } - async function gen1(): Awaitable { $r = await Batcher::fetch(1); echo "Got result for id 1: $r\n"; $r = await Batcher::fetch(2); echo "Got result for id 2: $r\n"; } - async function gen2(): Awaitable { $r = await Batcher::fetch(3); echo "Got result for id 3: $r\n"; } - async function run(): Awaitable { await GenArrayWaitHandle::create(array(gen1(), gen2())); } - function main(): void { run()->getWaitHandle()->join(); } - -/* HH_FIXME[1002]: To ignore type checker warning for top-level statements */ +// HH_FIXME[1002] ... To ignore type checker warning for top-level statements main(); diff --git a/__docs/phpdoc/sample-code/hack.async2.php b/__docs/phpdoc/sample-code/hack.async2.php new file mode 100644 index 00000000..2023956d --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.async2.php @@ -0,0 +1,32 @@ + { + if ($a === 0) { + return null; + } + + $bar = await gen_bar($a); + if ($bar !== null) { + return $bar->getFoo(); + } + + return null; +} + +async function gen_bar(int $a): Awaitable { + if ($a === 0) { + return null; + } + + return new Bar(); +} + + +gen_foo(4); diff --git a/__docs/phpdoc/sample-code/hack.async3.php b/__docs/phpdoc/sample-code/hack.async3.php new file mode 100644 index 00000000..8ce7f175 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.async3.php @@ -0,0 +1,10 @@ + { return new Foo(); } +async function cached_result(T $x): Awaitable { return $x; } +async function gen_void(): Awaitable { return; } +async function gen_add(Awaitable $genA, Awaitable $genB): Awaitable { + list($a, $b) = await genva($genA, $genB); + return $a + $b; +} +class Preparable implements Awaitable { ... } +class MyPreparable extends Preparable { ... } diff --git a/__docs/phpdoc/sample-code/hack.attributes1.php b/__docs/phpdoc/sample-code/hack.attributes1.php new file mode 100644 index 00000000..c6d288d0 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.attributes1.php @@ -0,0 +1,13 @@ +> + public function bar(string $key) { + return baz($key); + } +} + +$rc = new ReflectionClass('Foo'); +$attrs = $rc->getMethod('bar')->getAttributes(); +var_dump($attrs); +$attr = $rc->getMethod('bar')->getAttribute("key"); +var_dump($attr); diff --git a/__docs/phpdoc/sample-code/hack.attributes10.php b/__docs/phpdoc/sample-code/hack.attributes10.php new file mode 100644 index 00000000..9ec91cb0 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.attributes10.php @@ -0,0 +1,15 @@ +> +class Foo { + public static function make(): this { + return new static(); + } +} +class ChildOfFoo extends Foo { + public function __construct() {} +} + +function main() { + $child = ChildOfFoo::make(); +} +main(); diff --git a/__docs/phpdoc/sample-code/hack.attributes2.php b/__docs/phpdoc/sample-code/hack.attributes2.php new file mode 100644 index 00000000..396b3310 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.attributes2.php @@ -0,0 +1,20 @@ +// file1.php +implementation(); + } + protected function implementation(): void { + echo 'parent implementation', "\n"; + } +} + +// file2.php +> + protected function implementation(): void { + echo 'child implementation', "\n"; + } +} diff --git a/__docs/phpdoc/sample-code/hack.attributes3.php b/__docs/phpdoc/sample-code/hack.attributes3.php new file mode 100644 index 00000000..74596b8a --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.attributes3.php @@ -0,0 +1,7 @@ +// file1.php +> // checked on use classes + function foo(): void {} +} + +class C1 { + use T1; // error! foo is not an override +} + +class C2 { + function foo(): void {} +} + +class C3 extends C2 { + use T1; // OK! C2's implementation is being overridden +} diff --git a/__docs/phpdoc/sample-code/hack.attributes5.php b/__docs/phpdoc/sample-code/hack.attributes5.php new file mode 100644 index 00000000..52aaaecf --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.attributes5.php @@ -0,0 +1,12 @@ +cache[$key])) { + $this->cache[$key] = function_that_returns_baz($key); + } + return $this->cache[$key]; + } +} diff --git a/__docs/phpdoc/sample-code/hack.attributes6.php b/__docs/phpdoc/sample-code/hack.attributes6.php new file mode 100644 index 00000000..c8f10494 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.attributes6.php @@ -0,0 +1,8 @@ +> + public function bar(string $key): Baz { + return function_that_returns_baz($key); + } +} diff --git a/__docs/phpdoc/sample-code/hack.attributes7.php b/__docs/phpdoc/sample-code/hack.attributes7.php new file mode 100644 index 00000000..c012232c --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.attributes7.php @@ -0,0 +1,14 @@ +> + public function foo(string $key): Baz { + return another_function_that_returns_baz($key); + } +} diff --git a/__docs/phpdoc/sample-code/hack.attributes8.php b/__docs/phpdoc/sample-code/hack.attributes8.php new file mode 100644 index 00000000..73978a0a --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.attributes8.php @@ -0,0 +1,14 @@ +> +class Foo { + public static function make(): this { + return new static(); + } +} +class ChildOfFoo extends Foo { + public function __construct(private A $a) {} +} + +function main() { + $child = ChildOfFoo::make(); +} +main(); diff --git a/__docs/phpdoc/sample-code/hack.collections1.php b/__docs/phpdoc/sample-code/hack.collections1.php new file mode 100644 index 00000000..0527195b --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.collections1.php @@ -0,0 +1,29 @@ + "aaa", "b" => "bbb", "c" => "ccc"); + +// Array containing the values 1, 2, 3 but now indexed +// by a mix of integer keys and string keys +$arr4 = array("foo" => 1, 73 => 2, "bar" => 3); + +// Array having mixed-typed values, default indexed by +// integers. +$arr5 = array(1, "hello", array(2, 3), "goodbye"); + +// Dynamically grow arrays by just adding new values. The +// keys do not have to be sequential or of the same type. +$arr1[] = 4; // The key will be 3 +$arr1[4] = 5; +$arr2["bap"] = 6; +$arr3["d"] = "ddd"; +$arr4[] = "blah"; // The key will be 74 +$arr5[9] = 3; diff --git a/__docs/phpdoc/sample-code/hack.collections10.php b/__docs/phpdoc/sample-code/hack.collections10.php new file mode 100644 index 00000000..105a509d --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.collections10.php @@ -0,0 +1,13 @@ + 'foo', 73 => 'bar', 144 => 'baz'}; + var_dump($map); +} + +f(); + \ No newline at end of file diff --git a/__docs/phpdoc/sample-code/hack.collections13.php b/__docs/phpdoc/sample-code/hack.collections13.php new file mode 100644 index 00000000..32d9c6d4 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.collections13.php @@ -0,0 +1,20 @@ + 1, 'b' => 2}; + + // Each instance of class C will get its own copy + // of Vector {1, 2, 3} in property $foo + public $foo = Vector {1, 2, 3}; + + // Each invocation of h() with no parameters will + // return a distinct empty Vector + function h($x = Vector {}) { + return $x; + } + + function j() { + static $y = Map {1 => 'a', 2 => 'b'}; + return $y; + } +} + \ No newline at end of file diff --git a/__docs/phpdoc/sample-code/hack.collections14.php b/__docs/phpdoc/sample-code/hack.collections14.php new file mode 100644 index 00000000..9154ac03 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.collections14.php @@ -0,0 +1,13 @@ + $value) { + echo $key . " => " . $value . "\n"; + if ($key == 0) { + $v1[2] = 9; + } + } +} + +main(); diff --git a/__docs/phpdoc/sample-code/hack.collections16.php b/__docs/phpdoc/sample-code/hack.collections16.php new file mode 100644 index 00000000..972b1dae --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.collections16.php @@ -0,0 +1,15 @@ + 1, 'b' => 2, 'c' => 3, 'd' => 4}; + foreach ($m as $key => $value) { + echo $key . " => " . $value . "\n"; + if ($key == 'a') { + $m->remove('d'); + } + } +} + +main(); diff --git a/__docs/phpdoc/sample-code/hack.collections17.php b/__docs/phpdoc/sample-code/hack.collections17.php new file mode 100644 index 00000000..24d8ee6d --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.collections17.php @@ -0,0 +1,27 @@ +at(0)); + } catch (OutOfBoundsException $e) { + echo "Caught exception 2\n"; + } + + try { + var_dump($m->get(0)); + } catch (OutOfBoundsException $e) { + echo "Caught exception 3\n"; + } +} + +main(); diff --git a/__docs/phpdoc/sample-code/hack.collections18.php b/__docs/phpdoc/sample-code/hack.collections18.php new file mode 100644 index 00000000..5cdd3566 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.collections18.php @@ -0,0 +1,12 @@ + $m, string $k): int { + echo $m[$k] . "\n"; +} + +function main() { + $m = Map {'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4}; + foo($m, 'c'); +} diff --git a/__docs/phpdoc/sample-code/hack.collections19.php b/__docs/phpdoc/sample-code/hack.collections19.php new file mode 100644 index 00000000..dfb5d6ac --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.collections19.php @@ -0,0 +1,10 @@ + 1, 'b' => 2, 'c' => 3, 'd' => 4}; + $v = $m->keys(); + var_dump($v); +} + +main(); diff --git a/__docs/phpdoc/sample-code/hack.collections2.php b/__docs/phpdoc/sample-code/hack.collections2.php new file mode 100644 index 00000000..08f1cc72 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.collections2.php @@ -0,0 +1,5 @@ + 1, 'b' => 2, 'c' => 3, 'd' => 4}; + $m2 = $m->map(function(int $x):int { return $x + 10; }); + var_dump($m2); +} + +main(); diff --git a/__docs/phpdoc/sample-code/hack.collections21.php b/__docs/phpdoc/sample-code/hack.collections21.php new file mode 100644 index 00000000..c19c0224 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.collections21.php @@ -0,0 +1,11 @@ +// The filter() method can be used on any collection to +// get a concrete collection (usually the same type as +// the original) containing the values that meet some +// condition. +function main() { + $m = Map {'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4}; + $m2 = $m->filter(function(int $x):bool { return $x % 2 == 0; }); + var_dump($m2); +} + +main(); diff --git a/__docs/phpdoc/sample-code/hack.collections22.php b/__docs/phpdoc/sample-code/hack.collections22.php new file mode 100644 index 00000000..d1091a97 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.collections22.php @@ -0,0 +1,14 @@ + 1, 'b' => 2, 'c' => 3, 'd' => 4}; + $result = $m->filter(function(int $x):bool { return $x % 2 == 0; }) + ->map(function(int $x):int { return $x + 1; }); + foreach ($result as $key => $value) { + echo $key . " => " . $value . "\n"; + } +} + +main(); diff --git a/__docs/phpdoc/sample-code/hack.collections23.php b/__docs/phpdoc/sample-code/hack.collections23.php new file mode 100644 index 00000000..6ddb8a28 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.collections23.php @@ -0,0 +1,25 @@ + 1, 'b' => 2, 'c' => 3, 'd' => 4}; + $iterable = $m->items(); + $m['a'] = 100; + $i = 0; + foreach ($iterable as $t) { + echo $t[0] . " => " . $t[1] . "\n"; + if ($i == 2) { + echo "Removing key 'a'\n"; + $m->remove('a'); + } + ++$i; + } +} +main(); + diff --git a/__docs/phpdoc/sample-code/hack.collections24.php b/__docs/phpdoc/sample-code/hack.collections24.php new file mode 100644 index 00000000..c3a06bdd --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.collections24.php @@ -0,0 +1,8 @@ +addAll(Vector {22, 33, 44}); + var_dump($vec); +} + +main(); diff --git a/__docs/phpdoc/sample-code/hack.collections25.php b/__docs/phpdoc/sample-code/hack.collections25.php new file mode 100644 index 00000000..cc0d8abf --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.collections25.php @@ -0,0 +1,7 @@ +function main() { + $map = Map {'a' => 11}; + $map->setAll(Map {'b' => 22, 'c' => 33}); + var_dump($map); +} + +main(); diff --git a/__docs/phpdoc/sample-code/hack.collections26.php b/__docs/phpdoc/sample-code/hack.collections26.php new file mode 100644 index 00000000..2c2e3c4a --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.collections26.php @@ -0,0 +1,11 @@ + 11, 'b' => 22, 'c' => 33}; + $v = new Vector($m); + var_dump($v); +} + +main(); diff --git a/__docs/phpdoc/sample-code/hack.collections27.php b/__docs/phpdoc/sample-code/hack.collections27.php new file mode 100644 index 00000000..453b0309 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.collections27.php @@ -0,0 +1,21 @@ +(ConstCollection $in): OutputCollection { + $out = Vector {}; + if (!($in instanceof ConstVector)) { + return null; + } + foreach ($in->items() as $elm) { + if ($elm > 1) { + $out->add($elm); + } + } + return $out; +} + +function main(): void { + $x = Vector {1, 2, 3}; + var_dump(process_elements($x)); +} + +main(); diff --git a/__docs/phpdoc/sample-code/hack.collections28.php b/__docs/phpdoc/sample-code/hack.collections28.php new file mode 100644 index 00000000..229f61ac --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.collections28.php @@ -0,0 +1,13 @@ + $it) { .. } +function g() { yield 1; yield 2; } + +function main() { + $gen = g(); + foo($gen); +} + +main(); diff --git a/__docs/phpdoc/sample-code/hack.collections29.php b/__docs/phpdoc/sample-code/hack.collections29.php new file mode 100644 index 00000000..adfaa8d2 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.collections29.php @@ -0,0 +1,12 @@ + $y) { + if (array_key_exists($y, $g)) { + $ret[$x] = $g[$y]; + } + } + return $ret; +} diff --git a/__docs/phpdoc/sample-code/hack.collections3.php b/__docs/phpdoc/sample-code/hack.collections3.php new file mode 100644 index 00000000..2ac1bf48 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.collections3.php @@ -0,0 +1,19 @@ +add(15); + $vector->add(20); + + $vector[] = 25; + + $vector->removeKey(2); + + foreach ($vector as $item) { + echo $item . "\n"; + } +} + +main_col(); diff --git a/__docs/phpdoc/sample-code/hack.collections30.php b/__docs/phpdoc/sample-code/hack.collections30.php new file mode 100644 index 00000000..457940ac --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.collections30.php @@ -0,0 +1,17 @@ +( + Indexish $f, + Indexish $g +): array { + $ret = array(); + foreach ($f as $x => $y) { + if (array_key_exists($y, $g)) { + $ret[$x] = $g[$y]; + } + } + return $ret; +} + \ No newline at end of file diff --git a/__docs/phpdoc/sample-code/hack.collections31.php b/__docs/phpdoc/sample-code/hack.collections31.php new file mode 100644 index 00000000..ee3aaf10 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.collections31.php @@ -0,0 +1,17 @@ +( + Indexish $f, + Indexish $g +): Map { + $ret = Map {}; + foreach ($f as $x => $y) { + if (array_key_exists($y, $g)) { + $ret[$x] = $g[$y]; + } + } + return $ret; +} diff --git a/__docs/phpdoc/sample-code/hack.collections32.php b/__docs/phpdoc/sample-code/hack.collections32.php new file mode 100644 index 00000000..d0695192 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.collections32.php @@ -0,0 +1,18 @@ + 1, 1 => 2, 2 => 3, 3 => 4, 4 => 5}; + $map2 = Map {0 => 6, 1 => 7, 2 => 8, 3 => 9, 4 => 10}; + var_dump(array_compose($map1, $map2)); // original + var_dump(array_compose($map1, $map2)); // modified + var_dump(map_compose($map1, $map2)); +} + +main_indexish() diff --git a/__docs/phpdoc/sample-code/hack.collections4.php b/__docs/phpdoc/sample-code/hack.collections4.php new file mode 100644 index 00000000..10d8bb85 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.collections4.php @@ -0,0 +1,46 @@ +get(1) . "\n\n"; + + // Set value by key using "$c[$k]=$v" syntax, overwriting the previous + // value; note that "$c[$k]=$v" syntax for Vectors will throw an + // OutOfBoundsException if the key is out of bounds + $vector[0] = 999; + + // Remove an element by key + $vector->removeKey(2); + + // Iterate over the values using "foreach ($c as $v)" syntax + foreach ($vector as $v) { + echo $v . "\n"; + } + echo "\n"; + + // Iterate over the values using "for" and "$c[$x]" syntax + for ($i = 0; $i < count($vector); ++$i) { + echo $vector[$i] . "\n"; + } + + // Iterate over the keys and values using "foreach ($c as $k=>$v)" + // syntax + foreach ($vector as $k => $v) { + echo $k . ": " . $v . "\n"; + } + echo "\n"; +} + +main(); diff --git a/__docs/phpdoc/sample-code/hack.collections5.php b/__docs/phpdoc/sample-code/hack.collections5.php new file mode 100644 index 00000000..be3ea2fd --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.collections5.php @@ -0,0 +1,11 @@ +filter(function($x) { return $x >= 50; }); + foreach ($filtered_vec as $key => $val) { + echo $key . " " . $val . "\n"; + } +} + +main(); diff --git a/__docs/phpdoc/sample-code/hack.collections6.php b/__docs/phpdoc/sample-code/hack.collections6.php new file mode 100644 index 00000000..2873152b --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.collections6.php @@ -0,0 +1,40 @@ + 1, "B" => 2, "C" => 3}; + + // Add elements using "$c[$k]=$v" syntax; note that if $k is + // already present, "$c[$k]=$v" syntax will overwrite the previous + // value + $map["D"] = 4; + $map["E"] = 5; + + // Access value by key using "$c[$k]" syntax; note that "$c[$k]" + // syntax will throw an OutOfBoundsException if the key is not present + echo $map["A"] . "\n"; + + // Access value by key via get(); null will be returned if the key is + // not present + echo $map->get("B") . "\n\n"; + + // Remove element by key; if the key is not present the remove() + // method will do nothing and return + $map->remove("B"); + + // Testing if a key is present + echo ($map->contains("A") ? "true" : "false") . "\n\n"; + + // Iterate over the values using "foreach ($c as $v)" syntax + foreach ($map as $v) { + echo $v . "\n"; + } + + // Iterate over the keys and values using "foreach ($c as $k=>$v)" + // syntax + foreach ($map as $k => $v) { + echo $k . ": " . $v . "\n"; + } +} + +main(); // REMEMBER, insertion order is maintained. diff --git a/__docs/phpdoc/sample-code/hack.collections7.php b/__docs/phpdoc/sample-code/hack.collections7.php new file mode 100644 index 00000000..127b1ebc --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.collections7.php @@ -0,0 +1,8 @@ + 'a', 2 => 'b', 3 => 'c', 4 => 'd'}; + var_dump($m->filterWithKey(function($k, $v) { return $k >= 3; })); +} + +main(); diff --git a/__docs/phpdoc/sample-code/hack.collections8.php b/__docs/phpdoc/sample-code/hack.collections8.php new file mode 100644 index 00000000..790b8a9c --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.collections8.php @@ -0,0 +1,28 @@ +add("D")->add("E"); + + // Remove element by value + $set->remove("B"); + + // Testing if a value is present + echo ($set->contains("A") ? "true" : "false") . "\n\n"; + + // Iterate over the values using "foreach ($c as $v)" syntax + foreach ($set as $item) { + echo $item . "\n"; + } +} + +main(); diff --git a/__docs/phpdoc/sample-code/hack.collections9.php b/__docs/phpdoc/sample-code/hack.collections9.php new file mode 100644 index 00000000..4d1e4014 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.collections9.php @@ -0,0 +1,10 @@ +add(6); + $z = $s->RemoveAll($v); //difference between $v and $s + var_dump($s, $v, $z); +} + +foo(); diff --git a/__docs/phpdoc/sample-code/hack.constructorargumentpromotion1.php b/__docs/phpdoc/sample-code/hack.constructorargumentpromotion1.php new file mode 100644 index 00000000..d758af3a --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.constructorargumentpromotion1.php @@ -0,0 +1,11 @@ +name = $name; + $this->age = $age; + } +} diff --git a/__docs/phpdoc/sample-code/hack.constructorargumentpromotion2.php b/__docs/phpdoc/sample-code/hack.constructorargumentpromotion2.php new file mode 100644 index 00000000..407cf557 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.constructorargumentpromotion2.php @@ -0,0 +1,6 @@ + { + $i = 0; + while (true) { + yield $i++; + } +} + +$generator = yieldInfiniteInts(); +foreach ($generator as $value) { + echo "$value\n"; +} diff --git a/__docs/phpdoc/sample-code/hack.enums1.php b/__docs/phpdoc/sample-code/hack.enums1.php new file mode 100644 index 00000000..c07bc132 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.enums1.php @@ -0,0 +1,15 @@ + { + protected T $data; + + public function __construct(T $data) { + $this->data = $data; + } + + public function getData(): T { + return $this->data; + } +} diff --git a/__docs/phpdoc/sample-code/hack.generics10.php b/__docs/phpdoc/sample-code/hack.generics10.php new file mode 100644 index 00000000..e6b2d0b3 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.generics10.php @@ -0,0 +1,19 @@ + { + $x = new Vector(); + $x->add(new FooG()); + return $x; + } +} + +class BBG extends AAG { + protected function bar(): Vector { + $x = new Vector(); + $x->add(new FooGChild()); + return $x; + } +} diff --git a/__docs/phpdoc/sample-code/hack.generics11.php b/__docs/phpdoc/sample-code/hack.generics11.php new file mode 100644 index 00000000..7a80424b --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.generics11.php @@ -0,0 +1,32 @@ + { + private ?T $data; + + public function __construct() { + $this->data = null; + } + public function put(T $mail): void { + $this->data = $mail; + } + + public function check(): ?T { + if ($this->data !== null) { + return $this->data; + } + return null; + } +} + +function mbint(): Mailbox { + $mbi = new Mailbox(); + $mbi->put(3); + return $mbi; +} + +function mbmixed(Mailbox $mbm): void {} + +function main() { + $m = mbint(); + mbmixed($m); +} diff --git a/__docs/phpdoc/sample-code/hack.generics12.php b/__docs/phpdoc/sample-code/hack.generics12.php new file mode 100644 index 00000000..e52c7aa5 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.generics12.php @@ -0,0 +1,42 @@ +, Mailbox, Mailbox +class Mailbox { + private ?T $data; + + public function __construct() { + $this->data = null; + } + public function put(T $mail): void { + $this->data = $mail; + } + + public function check(): ?T { + if ($this->data !== null) { + return $this->data; + } + return null; + } +} + +function mbint(): Mailbox { + $mbi = new Mailbox(); + $mbi->put(3); + return $mbi; +} + +function mbmixed(Mailbox $mbm): void { + // Put a string into the mixed Mailbox + $mbm->put("Hello"); +} + +function main() { + $m = mbint(); + // This function puts a string into the Mailbox + mbmixed($m); + // Now what was a Mailbox becomes a Mailbox. Probably not expected behavior. + var_dump($m); +} + +main(); diff --git a/__docs/phpdoc/sample-code/hack.generics13.php b/__docs/phpdoc/sample-code/hack.generics13.php new file mode 100644 index 00000000..179eb1ab --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.generics13.php @@ -0,0 +1,39 @@ +, Mailbox, Mailbox +class Mailbox { + private ?T $data; + + public function __construct() { + $this->data = null; + } + public function put(T $mail): void { + $this->data = $mail; + } + + public function check(): ?T { + if ($this->data !== null) { + return $this->data; + } + return null; + } +} + +function mbint(): Mailbox { + $mbi = new Mailbox(); + $mbi->put(3); + return $mbi; +} + +function mbgen(Mailbox $mbm, T $item): void { + $mbm->put($item); +} + +function main() { + $m = mbint(); + mbgen($m, 4); + var_dump($m); +} + +main(); diff --git a/__docs/phpdoc/sample-code/hack.generics14.php b/__docs/phpdoc/sample-code/hack.generics14.php new file mode 100644 index 00000000..49df5cf4 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.generics14.php @@ -0,0 +1,42 @@ + { + private T $id; + public function __construct(T $id) { + $this->id = $id; + } + public function getID(): T { + return $this->id; + } +} + +interface IFoo {} + +class CA {} +class CB extends CA {} + +class Bar implements IFoo {} +class Baz implements IFoo {} +class Biz {} + +final class AnyIdentity extends Identity {} +final class CAIdentity extends Identity {} +final class CBIdentity extends Identity {} +final class FooIdentity extends Identity {} + +function main_constraints(): void { + $ai = new AnyIdentity("Hello"); + $ai2 = new AnyIdentity(new Biz()); + + $cb = new CBIdentity(new CB()); + $cb2 = new CBIdentity(new CA()); // HACK ERROR! + + $ca = new CAIdentity(new CA()); + $ca2 = new CAIdentity(new CB()); + + $fi = new FooIdentity(new Bar()); + $fi2 = new FooIdentity(new Baz()); + $fi3 = new FooIdentity(new Biz()); // HACK ERROR! +} + +main_constraints(); diff --git a/__docs/phpdoc/sample-code/hack.generics15.php b/__docs/phpdoc/sample-code/hack.generics15.php new file mode 100644 index 00000000..d8be1e20 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.generics15.php @@ -0,0 +1 @@ +$cb2 = new CBIdentity(new CA()); diff --git a/__docs/phpdoc/sample-code/hack.generics16.php b/__docs/phpdoc/sample-code/hack.generics16.php new file mode 100644 index 00000000..f720d352 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.generics16.php @@ -0,0 +1 @@ +$fi3 = new FooIdentity(new Biz()); diff --git a/__docs/phpdoc/sample-code/hack.generics17.php b/__docs/phpdoc/sample-code/hack.generics17.php new file mode 100644 index 00000000..5cd6283c --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.generics17.php @@ -0,0 +1,22 @@ +(T $x): T { + return $x; + } +} + +function main_constraints_gm(): void { + $bip = new Bip(); + $bip->get(new Faz()); + $bip->get(new Far()); + $bip->get(new NoIFaz()); // Hack error; +} + +main_constraints_gm(); diff --git a/__docs/phpdoc/sample-code/hack.generics18.php b/__docs/phpdoc/sample-code/hack.generics18.php new file mode 100644 index 00000000..65830c4a --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.generics18.php @@ -0,0 +1,39 @@ + { + private T $data; + public function __construct(T $x) { + $this->data = $x; + } + public function get(): T { + return $this->data; + } +} + +class BoxOfA extends Box { + private int $sum = 0; + public function __construct(T $e) { + parent::__construct($e); + $this->sum += $e->getVal(); + } +} + +function main_con(): void { + $b = new B(); + $box = new BoxOfA($b); + $b2 = $box->get(); + $b2->foo(); +} diff --git a/__docs/phpdoc/sample-code/hack.generics19.php b/__docs/phpdoc/sample-code/hack.generics19.php new file mode 100644 index 00000000..6751c0fe --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.generics19.php @@ -0,0 +1,47 @@ + { + private T $data; + public function __construct(T $x) { + $this->data = $x; + } + public function get(): T { + return $this->data; + } +} + +// class BoxOfA extends Box +class BoxOfA extends Box { + private int $sum = 0; + public function __construct(T $e) { + parent::__construct($e); + $this->sum += $e->getVal(); + } +} + +function main_con(): void { + $b = new B(); + var_dump($b); + $box = new BoxOfA($b); + var_dump($box); + $b2 = $box->get(); + var_dump($b2); + var_dump($b2->foo()); +} + +main_con(); diff --git a/__docs/phpdoc/sample-code/hack.generics2.php b/__docs/phpdoc/sample-code/hack.generics2.php new file mode 100644 index 00000000..addec607 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.generics2.php @@ -0,0 +1,12 @@ +getData()."\n"; + echo $gs->getData()."\n"; + echo $ga->getData()."\n"; +} + +main_gen(); diff --git a/__docs/phpdoc/sample-code/hack.generics20.php b/__docs/phpdoc/sample-code/hack.generics20.php new file mode 100644 index 00000000..6c2df5bd --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.generics20.php @@ -0,0 +1,14 @@ + { + public function __construct(T $t) {} +} +class A {} +class B extends A {} + +function foo(MyClass $x): void {} + +function bar(): void { + $b = new B(); + $x = new MyClass($b); + foo($x); +} diff --git a/__docs/phpdoc/sample-code/hack.generics21.php b/__docs/phpdoc/sample-code/hack.generics21.php new file mode 100644 index 00000000..217060ce --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.generics21.php @@ -0,0 +1,14 @@ + { + public function __construct(T $t) {} +} +class A {} +class B extends A {} + +function foo(MyClass $x): void {} + +function bar(): void { + $a = new A(); + $x = new MyClass($a); + foo($x); +} diff --git a/__docs/phpdoc/sample-code/hack.generics22.php b/__docs/phpdoc/sample-code/hack.generics22.php new file mode 100644 index 00000000..6bbba2f5 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.generics22.php @@ -0,0 +1,15 @@ + { + public function __construct(T $t) {} +} +class A {} +class B extends A {} +class C {} + +function foo(MyClass $x): void {} + +function bar(): void { + $c = new C(); + $x = new MyClass($c); + foo($x); +} diff --git a/__docs/phpdoc/sample-code/hack.generics23.php b/__docs/phpdoc/sample-code/hack.generics23.php new file mode 100644 index 00000000..a3c75c56 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.generics23.php @@ -0,0 +1,12 @@ + { + + public function getOpen(Vector $vec): T { + return $vec[0]; + } + + public function getClosed(Vector $vec): int { + return $vec[0]; + } +} diff --git a/__docs/phpdoc/sample-code/hack.generics24.php b/__docs/phpdoc/sample-code/hack.generics24.php new file mode 100644 index 00000000..20a68cd5 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.generics24.php @@ -0,0 +1,17 @@ +(T $a): void { + echo "foo()"; +} +function bar(T $a): int { + echo "bar()"; + return $a; +} + +function main_oct() { + $x = 5; + foo($x); + bar($x); +} + +main_oct(); diff --git a/__docs/phpdoc/sample-code/hack.generics25.php b/__docs/phpdoc/sample-code/hack.generics25.php new file mode 100644 index 00000000..70553a9a --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.generics25.php @@ -0,0 +1,18 @@ +(T $a): void { + $x = $a + 10; + echo "foo()"; +} +/*function bar(T $a): int { + echo "bar()"; + return $a; +}*/ + +function main_oct() { + $x = 5; + foo($x); + //bar($x); +} + +main_oct(); diff --git a/__docs/phpdoc/sample-code/hack.generics3.php b/__docs/phpdoc/sample-code/hack.generics3.php new file mode 100644 index 00000000..1eb39bd9 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.generics3.php @@ -0,0 +1,15 @@ + implements MutableCollection { +* : +* } +* +*/ + +function main_vec() { + $x = Vector {1, 2, 3, 4}; // T is associated with int + $y = Vector {'a', 'b', 'c', 'd'}; // T is associated with string +} + +main_vec(); diff --git a/__docs/phpdoc/sample-code/hack.generics4.php b/__docs/phpdoc/sample-code/hack.generics4.php new file mode 100644 index 00000000..f3dae153 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.generics4.php @@ -0,0 +1,29 @@ + { + public T $value; + public function __construct(T $v) { + $this->value = $v; + } +} + +class FooGenMethod { + public function swap(Box $a, Box $b) : void { + $temp = $a->value; + $a->value = $b->value; + $b->value = $temp; + } +} + +function main_genmeth() { + $f = new FooGenMethod(); + $y = new Box(3); + $z = new Box(4); + echo $y->value." ".$z->value; + $f->swap($y, $z); + echo $y->value." ".$z->value; +} + +main_genmeth(); diff --git a/__docs/phpdoc/sample-code/hack.generics5.php b/__docs/phpdoc/sample-code/hack.generics5.php new file mode 100644 index 00000000..8f1afab4 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.generics5.php @@ -0,0 +1,22 @@ +, Mailbox, Mailbox +class Mailbox { + private ?T $data; + + public function __construct() { + $this->data = null; + } + + public function put(T $mail): void { + $this->data = $mail; + } + + public function check(): ?T { + if ($this->data !== null) { + return $this->data; + } + return null; + } +} diff --git a/__docs/phpdoc/sample-code/hack.generics6.php b/__docs/phpdoc/sample-code/hack.generics6.php new file mode 100644 index 00000000..4a04d17e --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.generics6.php @@ -0,0 +1,60 @@ + { + public function add(T $item): void; + public function remove(): T; +} + +// generic trait +trait Commerce { + public function buy(T $item): void { + echo 'Bought a '.get_class($item)."\n"; + } + + public function sell(T $item): void { + echo 'Sold a '.get_class($item)."\n"; + } +} + +// generic class that uses generic trait and implements generic interface +class BestBuy implements Box { + protected Vector $vec; + private int $last; + + public function __construct() { + $this->vec = Vector{}; + $this->last = -1; + } + + use Commerce; + + public function add(T $item): void { + $this->vec->add($item); + $this->last++; + } + + public function remove(): T { + $item = $this->vec->at($this->last); + $this->vec->removeKey($this->last--); + return $item; + } +} + +// For example purposes +abstract class Computer {} +class Apple extends Computer{} +class Lenovo extends Computer {} +class Dell extends Computer {} + +function main_gti() { + $store = new BestBuy(); + $store->add(new Lenovo()); + $store->add(new Apple()); + $store->add(new Dell()); + echo get_class($store->remove())."\n"; + $store->sell($store->remove()); +} + +main_gti(); diff --git a/__docs/phpdoc/sample-code/hack.generics7.php b/__docs/phpdoc/sample-code/hack.generics7.php new file mode 100644 index 00000000..f0f30771 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.generics7.php @@ -0,0 +1,10 @@ +(Box $a, Box $b) : T { + $temp = $a->value; + $a->value = $b->value; + $b->value = $temp; + return $temp; + } +} diff --git a/__docs/phpdoc/sample-code/hack.generics8.php b/__docs/phpdoc/sample-code/hack.generics8.php new file mode 100644 index 00000000..581b16a9 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.generics8.php @@ -0,0 +1,28 @@ +add(new A()); + $x->add(new Z()); +} + +function foo_gi2(): void { + $x = Vector {}; + $x->add(new Z()); + $x->add(new A()); + foo_gi4($x); +} + +function foo_gi3(bool $b): void { + $x = Vector {}; + $x->add(new Z()); + $x->add(new A()); + foo_gi5($x); +} + +function foo_gi4(Vector $vec): void {} +function foo_gi5(Vector $vec): void {} +function foo_gi6(Vector $vec): void {} diff --git a/__docs/phpdoc/sample-code/hack.generics9.php b/__docs/phpdoc/sample-code/hack.generics9.php new file mode 100644 index 00000000..aa30fcd9 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.generics9.php @@ -0,0 +1,11 @@ +1, "b"=>2, "c"=>3); + $y = new ImmMap($x); + var_dump($y); +} + +main(); diff --git a/__docs/phpdoc/sample-code/hack.immsettv.construct1.php b/__docs/phpdoc/sample-code/hack.immsettv.construct1.php new file mode 100644 index 00000000..15a2d894 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.immsettv.construct1.php @@ -0,0 +1,9 @@ +Hello ".htmlize($name)."
"; +} diff --git a/__docs/phpdoc/sample-code/hack.intro2.php b/__docs/phpdoc/sample-code/hack.intro2.php new file mode 100644 index 00000000..3eda9ca8 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.intro2.php @@ -0,0 +1,9 @@ +Hello {$name}; +} diff --git a/__docs/phpdoc/sample-code/hack.lambda1.php b/__docs/phpdoc/sample-code/hack.lambda1.php new file mode 100644 index 00000000..29861174 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.lambda1.php @@ -0,0 +1,10 @@ + $y ==> $x * $z + $y; +$bar = $foo(5); +var_dump($bar(4)); // outputs 59 diff --git a/__docs/phpdoc/sample-code/hack.lambda11.php b/__docs/phpdoc/sample-code/hack.lambda11.php new file mode 100644 index 00000000..c5f2389e --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.lambda11.php @@ -0,0 +1,3 @@ + $captured . $k; diff --git a/__docs/phpdoc/sample-code/hack.lambda2.php b/__docs/phpdoc/sample-code/hack.lambda2.php new file mode 100644 index 00000000..3d22b202 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.lambda2.php @@ -0,0 +1,12 @@ + $a + $b; + // Do other stuff + // ..... + return $fn(10); +} + +var_dump(lam(42)); // return 52 diff --git a/__docs/phpdoc/sample-code/hack.lambda3.php b/__docs/phpdoc/sample-code/hack.lambda3.php new file mode 100644 index 00000000..1356f91d --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.lambda3.php @@ -0,0 +1,16 @@ + {$b = mult($b); return ($a + $b);}; + // Do other stuff + // ..... + return $fn(10); +} + +function mult(int $x): int { + return $x * 4; +} +var_dump(lam(42)); // return 178 diff --git a/__docs/phpdoc/sample-code/hack.lambda4.php b/__docs/phpdoc/sample-code/hack.lambda4.php new file mode 100644 index 00000000..ab29cc66 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.lambda4.php @@ -0,0 +1,8 @@ + $x . $y; +} +$fn = foo(); +echo $fn('baz'); // Outputs barbaz diff --git a/__docs/phpdoc/sample-code/hack.lambda5.php b/__docs/phpdoc/sample-code/hack.lambda5.php new file mode 100644 index 00000000..4ce8ce5a --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.lambda5.php @@ -0,0 +1,8 @@ + { return $x . $y; }; +} +$fn = foo(); +echo $fn('baz'); // Outputs barbaz diff --git a/__docs/phpdoc/sample-code/hack.lambda6.php b/__docs/phpdoc/sample-code/hack.lambda6.php new file mode 100644 index 00000000..3f091b00 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.lambda6.php @@ -0,0 +1,5 @@ + $y ==> $x + $y; +$g = $f(7); +echo $g(4); // Outputs 11 diff --git a/__docs/phpdoc/sample-code/hack.lambda7.php b/__docs/phpdoc/sample-code/hack.lambda7.php new file mode 100644 index 00000000..e6a648e0 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.lambda7.php @@ -0,0 +1,6 @@ + $x + 1; +$foo(12); // returns 13 + +$squared = array_map($x ==> $x*$x, array(1,2,3)); +var_dump($squared); // $squared is array(1,4,9) diff --git a/__docs/phpdoc/sample-code/hack.lambda8.php b/__docs/phpdoc/sample-code/hack.lambda8.php new file mode 100644 index 00000000..06d15d0a --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.lambda8.php @@ -0,0 +1,6 @@ + 73; +$foo(); // returns 73 + +$bar = ($x,$y) ==> $x + $y; +var_dump($bar(3,8)); // outputs 11 diff --git a/__docs/phpdoc/sample-code/hack.lambda9.php b/__docs/phpdoc/sample-code/hack.lambda9.php new file mode 100644 index 00000000..f5490bd3 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.lambda9.php @@ -0,0 +1,11 @@ + { + echo "Map $name has:\n"; + foreach ($x as $k => $v) { + echo " $k => $v\n"; + } +}; +$dump_map( + "My Map", + Map {'a' => 'b', 'c' => 'd'}, +); diff --git a/__docs/phpdoc/sample-code/hack.maptktv.construct1.php b/__docs/phpdoc/sample-code/hack.maptktv.construct1.php new file mode 100644 index 00000000..f6c9bfea --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.maptktv.construct1.php @@ -0,0 +1,9 @@ +1, "b"=>2, "c"=>3); + $y = new Map($x); + var_dump($y); +} + +main(); diff --git a/__docs/phpdoc/sample-code/hack.methoddispatch1.php b/__docs/phpdoc/sample-code/hack.methoddispatch1.php new file mode 100644 index 00000000..0243d3eb --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.methoddispatch1.php @@ -0,0 +1,67 @@ +f1(); + $a::f1(); + A::f2(); + $a->f2(); + $a::f2(); + + self::f1(); + B::f1(); + self::f2(); + B::f2(); + + C::f3(); + $c->f3(); + $c::f3(); + C::f4(); + $c->f4(); + $c::f4(); + + parent::f1(); + parent::f2(); + + static::f1(); + static::f2(); + + $this->f1(); + $this->f2(); + } +} + +class C { + public function f3(): void { + echo "C.f3()\n"; + } + public static function f4(): void { + echo "C.f4()\n"; + } +} + +function main() { + $b = new B(); + $b->test(); +} + +main(); diff --git a/__docs/phpdoc/sample-code/hack.methoddispatch2.php b/__docs/phpdoc/sample-code/hack.methoddispatch2.php new file mode 100644 index 00000000..ff719036 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.methoddispatch2.php @@ -0,0 +1,67 @@ +f1(); + $a::f1(); + A::f2(); + $a->f2(); + $a::f2(); + + self::f1(); + B::f1(); + self::f2(); + B::f2(); + + C::f3(); + $c->f3(); + $c::f3(); + C::f4(); + $c->f4(); + $c::f4(); + + parent::f1(); + parent::f2(); + + static::f1(); + static::f2(); + + $this->f1(); + $this->f2(); + } +} + +class C { + public function f3(): void { + echo "C.f3()\n"; + } + public static function f4(): void { + echo "C.f4()\n"; + } +} + +function main() { + B::stest(); +} + +main(); diff --git a/__docs/phpdoc/sample-code/hack.modes1.php b/__docs/phpdoc/sample-code/hack.modes1.php new file mode 100644 index 00000000..c07e9ff5 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.modes1.php @@ -0,0 +1,24 @@ +str = "Hello"; + } + + public function foo(int $x, int $y): int { + if ($x < $y) { + return 27; + } + return 34; + } + + public function bar(string $a, string $b): string { + return $a . $b; + } +} diff --git a/__docs/phpdoc/sample-code/hack.modes2.php b/__docs/phpdoc/sample-code/hack.modes2.php new file mode 100644 index 00000000..d8ca5365 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.modes2.php @@ -0,0 +1,14 @@ + $y) { + return 27; + } + return 34; +} + +// Not annotated. Not type checked. +function bar_partial($a, $b) { + return $a + $b; +} diff --git a/__docs/phpdoc/sample-code/hack.modes3.php b/__docs/phpdoc/sample-code/hack.modes3.php new file mode 100644 index 00000000..156f47a0 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.modes3.php @@ -0,0 +1,14 @@ + $y) { + return 27; + } + return 34; +} + +// Not annotated. Not type checked. +function bar_partial($a, $b) { + return $a + $b; +} diff --git a/__docs/phpdoc/sample-code/hack.modes4.php b/__docs/phpdoc/sample-code/hack.modes4.php new file mode 100644 index 00000000..06d65147 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.modes4.php @@ -0,0 +1,7 @@ + $y) { + // UNSAFE + return "I am not checked by the type checker"; // Covered by UNSAFE + } + return 34; // NOT covered by UNSAFE +} diff --git a/__docs/phpdoc/sample-code/hack.modes9.php b/__docs/phpdoc/sample-code/hack.modes9.php new file mode 100644 index 00000000..459ce207 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.modes9.php @@ -0,0 +1,11 @@ + $y) { + return "I am not checked by the type checker"; // Covered by the UNSAFE + } + return true; // Covered by the UNSAFE +} diff --git a/__docs/phpdoc/sample-code/hack.nullable1.php b/__docs/phpdoc/sample-code/hack.nullable1.php new file mode 100644 index 00000000..7399b614 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.nullable1.php @@ -0,0 +1,8 @@ + 5) { + return 5; + } else { + return null; + } + } + + public function nullableParameter(?int $x): int { + if (is_null($x)) { + return 100; + } else { + return -1; + } + } +} + +function main_nt() { + $nt = new NullableTest(); + $y = $nt->mayReturnNull(10); + var_dump($y); + $y = $nt->mayReturnNull(4); + var_dump($y); + + $z = $nt->nullableParameter(10); + var_dump($z); + $z = $nt->nullableParameter(null); + var_dump($z); +} + +main_nt(); diff --git a/__docs/phpdoc/sample-code/hack.nullable11.php b/__docs/phpdoc/sample-code/hack.nullable11.php new file mode 100644 index 00000000..39b4fda7 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.nullable11.php @@ -0,0 +1,33 @@ + 5) { + return 5; + } else { + return null; + } + } + + public function nullableParameter(int $x): int { + if (is_null($x)) { + return 100; + } else { + return -1; + } + } +} + +function main_nt() { + $nt = new NullableTest(); + $y = $nt->mayReturnNull(10); + var_dump($y); + $y = $nt->mayReturnNull(4); + var_dump($y); + + $z = $nt->nullableParameter(10); + var_dump($z); + $z = $nt->nullableParameter(null); + var_dump($z); +} + +main_nt(); diff --git a/__docs/phpdoc/sample-code/hack.nullable12.php b/__docs/phpdoc/sample-code/hack.nullable12.php new file mode 100644 index 00000000..894112e6 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.nullable12.php @@ -0,0 +1,35 @@ + 5) { + return 5; + } + else { + return null; + } + } + + public function nullableParameter(int $x = null): int { + if (is_null($x)) { + return 100; + } + else { + return -1; + } + } +} + +function main_nt() { + $nt = new NullableTest(); + $y = $nt->mayReturnNull(10); + var_dump($y); + $y = $nt->mayReturnNull(4); + var_dump($y); + + $z = $nt->nullableParameter(10); + var_dump($z); + $z = $nt->nullableParameter(null); + var_dump($z); +} + +main_nt(); diff --git a/__docs/phpdoc/sample-code/hack.nullable13.php b/__docs/phpdoc/sample-code/hack.nullable13.php new file mode 100644 index 00000000..f47fe7c0 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.nullable13.php @@ -0,0 +1,23 @@ +userDefinedTypeMayBeNull(null); + $y = $nb->userDefinedTypeMayBeNull(new UserDefinedType()); + var_dump($x); + var_dump($y); +} + +main_nt(); diff --git a/__docs/phpdoc/sample-code/hack.nullable14.php b/__docs/phpdoc/sample-code/hack.nullable14.php new file mode 100644 index 00000000..16eea6ad --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.nullable14.php @@ -0,0 +1,14 @@ + 4) { + return false; + } + return true; +} + +function bar(): bool { + return foo(5); + // In the past, you might have had to do this check + //$x = foo(5); + //if ($x !== null) { + // return $x; + //} else { + // throw; + //} +} diff --git a/__docs/phpdoc/sample-code/hack.nullable19.php b/__docs/phpdoc/sample-code/hack.nullable19.php new file mode 100644 index 00000000..98b7acc0 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.nullable19.php @@ -0,0 +1,28 @@ +x !== null) { + $a = $this->x->get(); + $b = $this->x->get(); + if ($b - $a < 1) { + return $this->x; + } + } + return new BizBang(); + } +} + +function main_nmv() { + $c = new NullMemVar(); + var_dump($c->foo()); +} + +main_nmv(); diff --git a/__docs/phpdoc/sample-code/hack.nullable2.php b/__docs/phpdoc/sample-code/hack.nullable2.php new file mode 100644 index 00000000..6900d485 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.nullable2.php @@ -0,0 +1,4 @@ +x; + if ($local_for_x !== null) { + $a = $local_for_x->get(); + $b = $local_for_x->get(); + if ($b - $a < 1) { + return $local_for_x; + } + } + return new BizBang(); + } +} + +function main_nmv() { + $c = new NullMemVar(); + var_dump($c->foo()); +} + +main_nmv(); diff --git a/__docs/phpdoc/sample-code/hack.nullable21.php b/__docs/phpdoc/sample-code/hack.nullable21.php new file mode 100644 index 00000000..e357fa6e --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.nullable21.php @@ -0,0 +1,27 @@ + 4) { + return $y + 5; + } else { + return null; + } + } +} + +class NullableNullThrowsTest { + protected int $x; + + public function __construct() { + $nnt = new NullableNullThrows(); + $nullable_value = $nnt->foo(2); + $this->x = $nullable_value; + } +} + +function main_nnt() { + $nntt = new NullableNullThrowsTest(); +} + +main_nnt(); diff --git a/__docs/phpdoc/sample-code/hack.nullable22.php b/__docs/phpdoc/sample-code/hack.nullable22.php new file mode 100644 index 00000000..60defadd --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.nullable22.php @@ -0,0 +1,8 @@ +(?T $x, ?string $message = null): T { + if ($x === null) { + throw new Exception($message ?: 'Unexpected null'); + } + + return $x; +} diff --git a/__docs/phpdoc/sample-code/hack.nullable23.php b/__docs/phpdoc/sample-code/hack.nullable23.php new file mode 100644 index 00000000..649a5288 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.nullable23.php @@ -0,0 +1,27 @@ + 4) { + return $y + 5; + } else { + return null; + } + } +} + +class NullableNullThrowsTest { + protected int $x; + + public function __construct() { + $nnt = new NullableNullThrows(); + $nullable_value = $nnt->foo(2); + $this->x = nullthrows($nullable_value); + } +} + +function main_nnt() { + $nntt = new NullableNullThrowsTest(); +} + +main_nnt(); diff --git a/__docs/phpdoc/sample-code/hack.nullable24.php b/__docs/phpdoc/sample-code/hack.nullable24.php new file mode 100644 index 00000000..c68166e2 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.nullable24.php @@ -0,0 +1,10 @@ +fluffy = get_bunny(); + pet_bunny($this->fluffy); + } +} diff --git a/__docs/phpdoc/sample-code/hack.nullable25.php b/__docs/phpdoc/sample-code/hack.nullable25.php new file mode 100644 index 00000000..7bff85d4 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.nullable25.php @@ -0,0 +1,13 @@ +fluffy = get_bunny(); + } + + public function pet(): void { + pet_bunny($this->fluffy); + } +} diff --git a/__docs/phpdoc/sample-code/hack.nullable26.php b/__docs/phpdoc/sample-code/hack.nullable26.php new file mode 100644 index 00000000..cf7bbc94 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.nullable26.php @@ -0,0 +1,19 @@ +fluffy; + if (!$fluffy) { + $fluffy = $this->fluffy = get_bunny(); + } + return $fluffy; + } + + public function pet(): void { + pet_bunny($this->getFluffy()); + } +} diff --git a/__docs/phpdoc/sample-code/hack.nullable27.php b/__docs/phpdoc/sample-code/hack.nullable27.php new file mode 100644 index 00000000..57918eb7 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.nullable27.php @@ -0,0 +1,15 @@ +i = $i; + var_dump($this); + } +} + +class IMVChild2 extends InitializeMemberVariables { + + protected string $s; + protected bool $b; + protected int $i; + + // Parent constructor called, setup called from there and dispatched to here + // to initialize member variables + protected function setup(string $s, bool $b, int $i) { + $this->s = $s; + $this->b = $b; + $this->i = $i; + var_dump($this); + } + +} + +$imvc = new IMVChild(4); +$imvc2 = new IMVChild2("Hi", true, 3); diff --git a/__docs/phpdoc/sample-code/hack.nullable3.php b/__docs/phpdoc/sample-code/hack.nullable3.php new file mode 100644 index 00000000..b67be5fd --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.nullable3.php @@ -0,0 +1,14 @@ +x === null) { + echo "Null 1"; + } + + if (is_null($this->x)) { + echo "Null 2"; + } + } +} diff --git a/__docs/phpdoc/sample-code/hack.nullable4.php b/__docs/phpdoc/sample-code/hack.nullable4.php new file mode 100644 index 00000000..b0fb43e3 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.nullable4.php @@ -0,0 +1,15 @@ +x === null) { + echo "Null 1"; + } + + if (is_null($this->x)) { + echo "Null 2"; + } + } +} diff --git a/__docs/phpdoc/sample-code/hack.nullable5.php b/__docs/phpdoc/sample-code/hack.nullable5.php new file mode 100644 index 00000000..6d4f1b22 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.nullable5.php @@ -0,0 +1,25 @@ +boolMayBeNull(true); + $y = $nb->boolMayBeNull(false); + $z = $nb->boolMayBeNull(null); + var_dump($x); + var_dump($y); + var_dump($z); +} + +main_nt(); diff --git a/__docs/phpdoc/sample-code/hack.nullable6.php b/__docs/phpdoc/sample-code/hack.nullable6.php new file mode 100644 index 00000000..4cd2d35f --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.nullable6.php @@ -0,0 +1,16 @@ +count(); + } +} + +function main_nt() { + $nna = new NullableNotAppropriate(); + $y = $nna->noNullableNeeded(); + var_dump($y); +} + +main_nt(); diff --git a/__docs/phpdoc/sample-code/hack.nullable7.php b/__docs/phpdoc/sample-code/hack.nullable7.php new file mode 100644 index 00000000..3f514386 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.nullable7.php @@ -0,0 +1,21 @@ + 5) { + return 5; + } else { + return null; + } + } +} + +function main_nt() { + $nt = new NullableTest(); + $y = $nt->mayReturnNull(10); + var_dump($y); + $y = $nt->mayReturnNull(4); + var_dump($y); +} + +main_nt(); diff --git a/__docs/phpdoc/sample-code/hack.nullable8.php b/__docs/phpdoc/sample-code/hack.nullable8.php new file mode 100644 index 00000000..68f219d5 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.nullable8.php @@ -0,0 +1,20 @@ + 5) { + return 5; + } else { + return null; + } + } +} + +function main_nt() { + $nt = new NullableTest(); + $y = $nt->mayReturnNull(10); + var_dump($y); + $y = $nt->mayReturnNull(4); + var_dump($y); +} + +main_nt(); diff --git a/__docs/phpdoc/sample-code/hack.nullable9.php b/__docs/phpdoc/sample-code/hack.nullable9.php new file mode 100644 index 00000000..7a7b8406 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.nullable9.php @@ -0,0 +1,20 @@ + 5) { + return
Hello World
; + } else { + return null; + } + } +} + +function main_nt() { + $nt = new NullableTest(); + $y = $nt->mayReturnNull(10); + var_dump($y); + $y = $nt->mayReturnNull(4); + var_dump($y); +} + +main_nt(); diff --git a/__docs/phpdoc/sample-code/hack.otherrulesandfeatures1.php b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures1.php new file mode 100644 index 00000000..7afbd0ae --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures1.php @@ -0,0 +1,17 @@ +x; + if (is_int($x)) { + $this->doSomething(); + $y = $x * 2; + } + } + + public function doSomething(): void {} + } diff --git a/__docs/phpdoc/sample-code/hack.otherrulesandfeatures11.php b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures11.php new file mode 100644 index 00000000..0dc03c41 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures11.php @@ -0,0 +1,11 @@ +x = 10; + } +} diff --git a/__docs/phpdoc/sample-code/hack.otherrulesandfeatures12.php b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures12.php new file mode 100644 index 00000000..c8bb7425 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures12.php @@ -0,0 +1,4 @@ +x = 10; + } +} diff --git a/__docs/phpdoc/sample-code/hack.otherrulesandfeatures15.php b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures15.php new file mode 100644 index 00000000..cbef4bf1 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures15.php @@ -0,0 +1,12 @@ +x = 10; + $this->foo(); + } + protected function foo() { + ... + } +} diff --git a/__docs/phpdoc/sample-code/hack.otherrulesandfeatures16.php b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures16.php new file mode 100644 index 00000000..bbfd8d2d --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures16.php @@ -0,0 +1,10 @@ +testFun1(); + $f->testFun2(); +} + +main_cufff(); diff --git a/__docs/phpdoc/sample-code/hack.otherrulesandfeatures18.php b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures18.php new file mode 100644 index 00000000..35c4e12a --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures18.php @@ -0,0 +1 @@ +var_dump(call_user_func('cufun1', 3)); diff --git a/__docs/phpdoc/sample-code/hack.otherrulesandfeatures19.php b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures19.php new file mode 100644 index 00000000..47b0b864 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures19.php @@ -0,0 +1,29 @@ +testFun1(); + $f->testFun2(); +} + +main_fff(); diff --git a/__docs/phpdoc/sample-code/hack.otherrulesandfeatures2.php b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures2.php new file mode 100644 index 00000000..e5cc4ff3 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures2.php @@ -0,0 +1,16 @@ +yay!"; + } +} + +function baz(int $a): I { + return $a === 1 ? new A() : new B(); +} + +function bar(): B { + $iface = baz(2); + invariant($iface instanceof B, "must be a B"); + $iface->yay(); + return $iface; +} + +bar(); diff --git a/__docs/phpdoc/sample-code/hack.otherrulesandfeatures27.php b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures27.php new file mode 100644 index 00000000..04cbff25 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures27.php @@ -0,0 +1,25 @@ +foo_method(); // Hack now understands that $untyped_array is an array + return true; +} + +bar(); diff --git a/__docs/phpdoc/sample-code/hack.otherrulesandfeatures28.php b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures28.php new file mode 100644 index 00000000..11742e7f --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures28.php @@ -0,0 +1,12 @@ +doOtherInit(); + $this->fluffy = new FluffyBunny(); + } + + protected function doOtherInit(): void { } +} diff --git a/__docs/phpdoc/sample-code/hack.otherrulesandfeatures29.php b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures29.php new file mode 100644 index 00000000..f0b965ed --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures29.php @@ -0,0 +1,12 @@ +doOtherInit(); + $this->fluffy = new FluffyBunny(); + } + + private function doOtherInit(): void { } +} diff --git a/__docs/phpdoc/sample-code/hack.otherrulesandfeatures3.php b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures3.php new file mode 100644 index 00000000..3937841e --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures3.php @@ -0,0 +1,4 @@ +utBar(); + } + $a->utFoo(); +} + +function main_ut() { + $b = new UTB(); + ut_xyz($b); +} + +main_ut(); diff --git a/__docs/phpdoc/sample-code/hack.otherrulesandfeatures31.php b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures31.php new file mode 100644 index 00000000..1eeeda43 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures31.php @@ -0,0 +1,32 @@ +utBar(); + } + $a->utFoo(); +} + +function main_ut() { + $b = new UTB(); + ut_xyz($b); +} + +main_ut(); diff --git a/__docs/phpdoc/sample-code/hack.otherrulesandfeatures32.php b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures32.php new file mode 100644 index 00000000..dbce34bf --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures32.php @@ -0,0 +1,8 @@ +bar[1]}. +This should not print a capital 'A': \x41 +EOT; +} + +foo(); diff --git a/__docs/phpdoc/sample-code/hack.otherrulesandfeatures36.php b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures36.php new file mode 100644 index 00000000..34405c24 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures36.php @@ -0,0 +1,8 @@ +find(Entity\User::class, 5); +// instead of +$user = $entityManager->find('MyVendor\SomeComponent\TargetEntityNs\User', 5); diff --git a/__docs/phpdoc/sample-code/hack.otherrulesandfeatures4.php b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures4.php new file mode 100644 index 00000000..c709e4ef --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures4.php @@ -0,0 +1,4 @@ +blah(); + ... + } else { + // $x is still just a Foo + ... + } +} diff --git a/__docs/phpdoc/sample-code/hack.otherrulesandfeatures8.php b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures8.php new file mode 100644 index 00000000..cd004310 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures8.php @@ -0,0 +1,12 @@ +doSomething(); + $y = $x * 2; + } + } + + public function doSomething(): void {} +} diff --git a/__docs/phpdoc/sample-code/hack.otherrulesandfeatures9.php b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures9.php new file mode 100644 index 00000000..a0721072 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.otherrulesandfeatures9.php @@ -0,0 +1,16 @@ +x)) { + $this->doSomething(); + // can no longer assume $this->x is an int, doSomething might have changed it back to null. + // note: can't analyze doSomething() because a child class of Foo might change its behavior. + $y = $this->x * 2; + } + } + + public function doSomething(): void {} +} diff --git a/__docs/phpdoc/sample-code/hack.settv.construct1.php b/__docs/phpdoc/sample-code/hack.settv.construct1.php new file mode 100644 index 00000000..34852255 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.settv.construct1.php @@ -0,0 +1,9 @@ + null, 'url' => null, 'count' => 0); + $my_struct['id'] = '573065673A34Y'; + $my_struct['url'] = 'http://facebook.com'; + + var_dump($my_struct); + $my_struct = why_shapes($my_struct); + var_dump($my_struct); +} + +main_why_shapes(); diff --git a/__docs/phpdoc/sample-code/hack.shapes2.php b/__docs/phpdoc/sample-code/hack.shapes2.php new file mode 100644 index 00000000..16462af5 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.shapes2.php @@ -0,0 +1,3 @@ + , 'id2' => ); +function foo(MyShape $x): void {} diff --git a/__docs/phpdoc/sample-code/hack.shapes3.php b/__docs/phpdoc/sample-code/hack.shapes3.php new file mode 100644 index 00000000..d10278c6 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.shapes3.php @@ -0,0 +1,3 @@ + , 'id2' => ); +function foo(MyShape $x): void {} diff --git a/__docs/phpdoc/sample-code/hack.shapes4.php b/__docs/phpdoc/sample-code/hack.shapes4.php new file mode 100644 index 00000000..1405e1a4 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.shapes4.php @@ -0,0 +1,15 @@ + int, 'y' => int); + +function dotProduct(Point2D $a, Point2D $b): int { + var_dump($a); + var_dump($b); + return $a['x'] * $b['x'] + $a['y'] * $b['y']; +} + +function main_sse(): void { + echo dotProduct(shape('x' => 3, 'y' => 3), shape('x' => 4, 'y' => 4)); +} + +main_sse(); diff --git a/__docs/phpdoc/sample-code/hack.shapes5.php b/__docs/phpdoc/sample-code/hack.shapes5.php new file mode 100644 index 00000000..5d3e0f9f --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.shapes5.php @@ -0,0 +1,14 @@ + string, 'url' => string, 'count' => int); + +function foo_saf(RandomData $rd): RandomData { + if ($rd['id'] === '573065673A34Z') { + $rd['count']++; + } + else { + $rd['url'] = "http://google.com"; + $rd['count']--; + } + return $rd; +} diff --git a/__docs/phpdoc/sample-code/hack.shapes6.php b/__docs/phpdoc/sample-code/hack.shapes6.php new file mode 100644 index 00000000..d79feb8e --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.shapes6.php @@ -0,0 +1,15 @@ + null, 'url' => null, 'count' => 0); + $rd['id'] = '573065673A34Y'; + $rd['url'] = 'http://facebook.com'; + var_dump($rd); + var_dump(foo_saf($rd)); + + + // This should cause a Hack error + // But this will still run in HHVM + $rd['count'] = 'I should error'; + var_dump(foo_saf($rd)); +} diff --git a/__docs/phpdoc/sample-code/hack.shapes7.php b/__docs/phpdoc/sample-code/hack.shapes7.php new file mode 100644 index 00000000..54f9d31a --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.shapes7.php @@ -0,0 +1,10 @@ + = shape ('x' => T, 'y' => T); + +function gen_shape_add(Point $pt1, Point $pt2): Point { + $sumx = $pt1['x'] + $pt2['x']; + $sumy = $pt1['y'] + $pt2['y']; + return shape ('x' => $sumx, 'y' => $sumy); +} + +function main_gs() { + // Float based shape + $pt1 = shape('x' => 1.0, 'y' => 2.0); + $pt2 = shape('x' => 3.0, 'y' => 4.0); + var_dump(gen_shape_add($pt1, $pt2)); + + // Int based shape + $pt3 = shape('x' => 1, 'y' => 2); + $pt4 = shape('x' => 3, 'y' => 4); + var_dump(gen_shape_add($pt3, $pt4)); +} + +main_gs(); diff --git a/__docs/phpdoc/sample-code/hack.traits1.php b/__docs/phpdoc/sample-code/hack.traits1.php new file mode 100644 index 00000000..bee438d5 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.traits1.php @@ -0,0 +1,14 @@ +x; + } + + public function getBin(Vector $vec): string { + return $vec[0]; + } +} diff --git a/__docs/phpdoc/sample-code/hack.traits2.php b/__docs/phpdoc/sample-code/hack.traits2.php new file mode 100644 index 00000000..75309767 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.traits2.php @@ -0,0 +1,28 @@ +bar(); + } +} + +class AAA { + protected function bar(): int { + return 5; + } +} + +class BBB extends AAA { + use TTT; + + public function baz(): int { + return $this->foo(); + } +} + +function main_t(): void { + $bbb = new BBB(); + var_dump($bbb->baz()); +} + +main_t(); diff --git a/__docs/phpdoc/sample-code/hack.traits3.php b/__docs/phpdoc/sample-code/hack.traits3.php new file mode 100644 index 00000000..84463db6 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.traits3.php @@ -0,0 +1,29 @@ +bar(); + } +} + +class AAA { + protected function bar(): int { + return 5; + } +} + +class BBB extends AAA { + use TTT; + + public function baz(): int { + return $this->foo(); + } +} + +function main_t(): void { + $bbb = new BBB(); + var_dump($bbb->baz()); +} + +main_t(); diff --git a/__docs/phpdoc/sample-code/hack.traits4.php b/__docs/phpdoc/sample-code/hack.traits4.php new file mode 100644 index 00000000..085fa7fd --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.traits4.php @@ -0,0 +1,47 @@ + $vec): int; + public function get(): int; +} + +trait Foo implements Bar { + private int $x = 5; + + private function getVal(): int { + return $this->x; + } + + public function get(): int { + return $this->getVal(); + } + + public function babs(Vector $vec): int { + if (count($vec) > 0) { + return $vec[0]; + } + return -1; + } +} + +class Baz implements Bar { + use Foo; + + private Vector $v; + + public function __construct() { + $this->v = Vector {}; + $this->v[] = $this->get(); + } + + public function sass(): int { + return $this->babs($this->v); + } +} + +function main_traits() { + $b = new Baz(); + var_dump($b->sass()); +} + +main_traits(); diff --git a/__docs/phpdoc/sample-code/hack.traits5.php b/__docs/phpdoc/sample-code/hack.traits5.php new file mode 100644 index 00000000..74a6b77c --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.traits5.php @@ -0,0 +1,16 @@ +test(); +} + +main_tup(); diff --git a/__docs/phpdoc/sample-code/hack.tuples2.php b/__docs/phpdoc/sample-code/hack.tuples2.php new file mode 100644 index 00000000..adc01bd2 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.tuples2.php @@ -0,0 +1,5 @@ + { + $vec = Vector{$tup}; + return $vec; +} diff --git a/__docs/phpdoc/sample-code/hack.tuples6.php b/__docs/phpdoc/sample-code/hack.tuples6.php new file mode 100644 index 00000000..47d75b73 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.tuples6.php @@ -0,0 +1,34 @@ + { + $arr = array("Hello", 3); + $arr[2] = 4; + var_dump($arr); + return $arr; + } + + public function bar(): Vector { + $vec = Vector {"Hello", 3}; + $vec->add(4); + var_dump($vec); + return $vec; + } + + // This is how a tuple is returned from a method + public function baz(): (string, int) { + $tup = tuple("Hello", 3); + //$tup[2] = 4; + return $tup; + } +} + +function main_tup() { + $rmv = new ReturnMultipleValues(); + $rmv->foo(); + $rmv->bar(); + $rmv->baz(); +} + +main_tup(); diff --git a/__docs/phpdoc/sample-code/hack.tuples7.php b/__docs/phpdoc/sample-code/hack.tuples7.php new file mode 100644 index 00000000..4c1846f3 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.tuples7.php @@ -0,0 +1,5 @@ + as T = T; diff --git a/__docs/phpdoc/sample-code/hack.typealiasing14.php b/__docs/phpdoc/sample-code/hack.typealiasing14.php new file mode 100644 index 00000000..ec3d1fea --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.typealiasing14.php @@ -0,0 +1,13 @@ +s = 464; + $m = TypeDefsConv::convertSecondsToMinutes($this->s); + echo $m; + TypeDefsConv::funcForMinutes($m); + } +} + +function main_tdc(): void { + $utdc = new UseTypeDefsConv(); + $utdc->foo(); +} + +main_tdc(); diff --git a/__docs/phpdoc/sample-code/hack.typealiasing22.php b/__docs/phpdoc/sample-code/hack.typealiasing22.php new file mode 100644 index 00000000..774a8e61 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.typealiasing22.php @@ -0,0 +1,7 @@ + = Vector>; + +function bar_ta_gen(Matrix $x): void { + var_dump($x); +} diff --git a/__docs/phpdoc/sample-code/hack.typealiasing23.php b/__docs/phpdoc/sample-code/hack.typealiasing23.php new file mode 100644 index 00000000..55c85ab8 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.typealiasing23.php @@ -0,0 +1,14 @@ +> $x): void { + bar_ta_gen($x); // Vector> is identical to Matrix +} + +function foo_ta_gen_main(): void { + $vecvec = Vector {Vector {1.0, 2.0}, Vector {3.0, 4.0}}; + foo_ta_gen($vecvec); +} + +foo_ta_gen_main(); diff --git a/__docs/phpdoc/sample-code/hack.typealiasing24.php b/__docs/phpdoc/sample-code/hack.typealiasing24.php new file mode 100644 index 00000000..c1cd144c --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.typealiasing24.php @@ -0,0 +1,26 @@ + = string; + +class FooPhantom {} +function serialize_phantom(T $t): Serialized { + return serialize($t); +} +function unserialize_phantom(Serialized $s): T { + return unserialize($s); +} + +// Using the api: +function main_phantom(): void { + $x = new FooPhantom(); + var_dump($x); + // $serialized is a Serialized, aka "string" + $serialized = serialize_phantom($x); + var_dump($serialized); + // we now know the type of $y must be Foo + $y = unserialize_phantom($serialized); + var_dump($y); +} + +main_phantom(); diff --git a/__docs/phpdoc/sample-code/hack.typealiasing25.php b/__docs/phpdoc/sample-code/hack.typealiasing25.php new file mode 100644 index 00000000..21ac217e --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.typealiasing25.php @@ -0,0 +1,15 @@ + { + const Color BLUE = 1; + const Color RED = 2; + const Color GREEN = 3; + + public static function getColor(Color $color) { + switch ($color) { + case 1: return "0000ff"; + case 2: return "ff0000"; + case 3: return "00ff00"; + } +} diff --git a/__docs/phpdoc/sample-code/hack.typealiasing26.php b/__docs/phpdoc/sample-code/hack.typealiasing26.php new file mode 100644 index 00000000..b6a8307d --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.typealiasing26.php @@ -0,0 +1,9 @@ + + This is really a silly example. I hope you will not actually write code like this... + ; +} diff --git a/__docs/phpdoc/sample-code/hack.typealiasing3.php b/__docs/phpdoc/sample-code/hack.typealiasing3.php new file mode 100644 index 00000000..293ca713 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.typealiasing3.php @@ -0,0 +1,2 @@ + +$div->appendChild($foo); diff --git a/__docs/phpdoc/sample-code/hack.xhp3.php b/__docs/phpdoc/sample-code/hack.xhp3.php new file mode 100644 index 00000000..42fdbf7e --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.xhp3.php @@ -0,0 +1,4 @@ +{$msg}; +} diff --git a/__docs/phpdoc/sample-code/hack.xhp4.php b/__docs/phpdoc/sample-code/hack.xhp4.php new file mode 100644 index 00000000..dd0f1928 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.xhp4.php @@ -0,0 +1,4 @@ +getCurrentCount() +} diff --git a/__docs/phpdoc/sample-code/hack.xhp5.php b/__docs/phpdoc/sample-code/hack.xhp5.php new file mode 100644 index 00000000..f517eac9 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.xhp5.php @@ -0,0 +1,9 @@ + 4) { + return "some string"; + } else { + $div =
; + return $div; // XHPChild + } +} diff --git a/__docs/phpdoc/sample-code/hack.xhp6.php b/__docs/phpdoc/sample-code/hack.xhp6.php new file mode 100644 index 00000000..4598ce25 --- /dev/null +++ b/__docs/phpdoc/sample-code/hack.xhp6.php @@ -0,0 +1,5 @@ +; +$div->setAttribute('class', 1); // Type error +$div->getAttribute('class')->someMethod(); // Type error +$num = ; // Type error