mirror of
				https://github.com/KevinMidboe/linguist.git
				synced 2025-10-29 17:50:22 +00:00 
			
		
		
		
	| @@ -25,6 +25,9 @@ module Linguist | ||||
|         if languages.all? { |l| ["Common Lisp", "OpenCL"].include?(l) } | ||||
|           result = disambiguate_cl(data, languages) | ||||
|         end | ||||
|         if languages.all? { |l| ["Hack", "PHP"].include?(l) } | ||||
|           result = disambiguate_hack(data, languages) | ||||
|         end | ||||
|         if languages.all? { |l| ["Scala", "SuperCollider"].include?(l) } | ||||
|           result = disambiguate_sc(data, languages) | ||||
|         end | ||||
| @@ -94,6 +97,16 @@ module Linguist | ||||
|       matches | ||||
|     end | ||||
|  | ||||
|     def self.disambiguate_hack(data, languages) | ||||
|       matches = [] | ||||
|       if data.include?("<?hh") | ||||
|         matches << Language["Hack"] | ||||
|       elsif /<?[^h]/.match(data) | ||||
|         matches << Language["PHP"] | ||||
|       end | ||||
|       matches | ||||
|     end | ||||
|  | ||||
|     def self.disambiguate_sc(data, languages) | ||||
|       matches = [] | ||||
|       if (/\^(this|super)\./.match(data) || /^\s*(\+|\*)\s*\w+\s*{/.match(data) || /^\s*~\w+\s*=\./.match(data)) | ||||
|   | ||||
| @@ -1021,6 +1021,14 @@ HTTP: | ||||
|   extensions: | ||||
|   - .http | ||||
|  | ||||
| Hack: | ||||
|   type: programming | ||||
|   lexer: PHP | ||||
|   ace_mode: php | ||||
|   extensions: | ||||
|   - .hh | ||||
|   - .php | ||||
|  | ||||
| Haml: | ||||
|   group: HTML | ||||
|   type: markup | ||||
|   | ||||
							
								
								
									
										10
									
								
								samples/C++/bar.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								samples/C++/bar.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| class Bar | ||||
| { | ||||
|   protected: | ||||
|  | ||||
|     char *name; | ||||
|  | ||||
|   public: | ||||
|  | ||||
|     void hello(); | ||||
| } | ||||
							
								
								
									
										55
									
								
								samples/Hack/Assert.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								samples/Hack/Assert.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| <?hh // strict | ||||
| /** | ||||
|  * Copyright (c) 2014, Facebook, Inc. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * This source code is licensed under the BSD-style license found in the | ||||
|  * LICENSE file in the root directory of this source tree. An additional grant | ||||
|  * of patent rights can be found in the PATENTS file in the same directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| final class AssertException extends Exception {} | ||||
|  | ||||
| final class Assert { | ||||
|   public static function isNum(mixed $x): num { | ||||
|     if (is_float($x)) { | ||||
|       return $x; | ||||
|     } else if (is_int($x)) { | ||||
|       return $x; | ||||
|     } | ||||
|     throw new AssertException('Expected an int or float value'); | ||||
|   } | ||||
|  | ||||
|   public static function isInt(mixed $x): int { | ||||
|     if (is_int($x)) { | ||||
|       return $x; | ||||
|     } | ||||
|     throw new AssertException('Expected an int'); | ||||
|   } | ||||
|  | ||||
|   public static function isFloat(mixed $x): float { | ||||
|     if (is_float($x)) { | ||||
|       return $x; | ||||
|     } | ||||
|     throw new AssertException('Expected a float'); | ||||
|   } | ||||
|  | ||||
|   public static function isString(mixed $x): string { | ||||
|     if (is_string($x)) { | ||||
|       return $x; | ||||
|     } | ||||
|     throw new AssertException('Expected a string'); | ||||
|   } | ||||
|  | ||||
|   // For arrays you need to check every element | ||||
|   public static function isArrayOf<T>( | ||||
|     (function(mixed): T) $fn, | ||||
|     mixed $x, | ||||
|   ): array<T> { | ||||
|     if (is_array($x)) { | ||||
|       return array_map($fn, $x); | ||||
|     } | ||||
|     throw new AssertException('Expected an array'); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										52
									
								
								samples/Hack/AssertRecipe.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								samples/Hack/AssertRecipe.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| <?hh // strict | ||||
| /** | ||||
|  * Copyright (c) 2014, Facebook, Inc. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * This source code is licensed under the BSD-style license found in the | ||||
|  * LICENSE file in the root directory of this source tree. An additional grant | ||||
|  * of patent rights can be found in the PATENTS file in the same directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| require_once $_SERVER['DOCUMENT_ROOT'].'/core/controller/recipe/init.php'; | ||||
| require_once "demo.php"; | ||||
|  | ||||
| class AssertRecipe extends Recipe implements RecipeWithDemo { | ||||
|  | ||||
|   protected function getName(): string { | ||||
|     return 'Assert'; | ||||
|   } | ||||
|  | ||||
|   <<Override>> | ||||
|   protected function getDescription(): ?string { | ||||
|     return 'When you have values with unknown types, it is useful to make '. | ||||
|       'some runtime assertions and have the type checker understand. This '. | ||||
|       'recipe demonstrates one approach.'; | ||||
|   } | ||||
|  | ||||
|   protected function getFilenames(): Vector<string> { | ||||
|     return Vector { | ||||
|       'Assert.php', | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   protected function getDocs(): Vector<(string, string)> { | ||||
|     return Vector{ | ||||
|       tuple ('Mixed Types', 'hack.annotations.mixedtypes'), | ||||
|       tuple ('Type Inference', 'hack.otherrulesandfeatures.typeinference'), | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   public function getDemoFilename(): string { | ||||
|     return 'demo.php'; | ||||
|   } | ||||
|  | ||||
|   public function getDemoResult(): string { | ||||
|     return assert_main(); | ||||
|   } | ||||
|  | ||||
|   public function getDemoXHP(): ?:xhp { | ||||
|     return null; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										39
									
								
								samples/Hack/Controller.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								samples/Hack/Controller.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| <?hh // strict | ||||
| /** | ||||
|  * Copyright (c) 2014, Facebook, Inc. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * This source code is licensed under the BSD-style license found in the | ||||
|  * LICENSE file in the root directory of this source tree. An additional grant | ||||
|  * of patent rights can be found in the PATENTS file in the same directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| require_once $_SERVER['DOCUMENT_ROOT'].'/core/startup/init.php'; | ||||
|  | ||||
| abstract class Controller { | ||||
|   protected function __construct() { | ||||
|     startup(); | ||||
|   } | ||||
|  | ||||
|   abstract protected function getCSS(): Set<string>; | ||||
|   abstract protected function getJS(): Set<string>; | ||||
|   abstract protected function getTitle(): string; | ||||
|   abstract protected function render(): :xhp; | ||||
|  | ||||
|   final protected function getHead(): :xhp { | ||||
|     $css = $this->getCSS()->toVector()->map( | ||||
|       ($css) ==> <link rel="stylesheet" type="text/css" href={$css} /> | ||||
|     ); | ||||
|     $js = $this->getJS()->toVector()->map( | ||||
|       ($js) ==> <script src={$js} /> | ||||
|     ); | ||||
|     return | ||||
|       <head> | ||||
|       <meta http-equiv="content-type" content="text/html; charset=UTF-8"/> | ||||
|         <title>{$this->getTitle()}</title> | ||||
|         {$css->toArray()} | ||||
|         {$js->toArray()} | ||||
|       </head>; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										52
									
								
								samples/Hack/DBResultRecipe.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								samples/Hack/DBResultRecipe.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| <?hh // strict | ||||
| /** | ||||
|  * Copyright (c) 2014, Facebook, Inc. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * This source code is licensed under the BSD-style license found in the | ||||
|  * LICENSE file in the root directory of this source tree. An additional grant | ||||
|  * of patent rights can be found in the PATENTS file in the same directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| require_once $_SERVER['DOCUMENT_ROOT'].'/core/controller/recipe/init.php'; | ||||
| require_once "demo.php"; | ||||
|  | ||||
| class DBResultRecipe extends Recipe implements RecipeWithDemo { | ||||
|  | ||||
|   protected function getName(): string { | ||||
|     return 'DB Result'; | ||||
|   } | ||||
|  | ||||
|   <<Override>> | ||||
|   protected function getDescription(): ?string { | ||||
|     return 'Fetching data from a DB introduces a few typing challenges. '. | ||||
|       'First, the data comes back untyped. Second, a row in a DB generally '. | ||||
|       'contains columns of different types.'; | ||||
|   } | ||||
|  | ||||
|   protected function getFilenames(): Vector<string> { | ||||
|     return Vector { | ||||
|       'FakeDB.php', | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   protected function getDocs(): Vector<(string, string)> { | ||||
|     return Vector{ | ||||
|       tuple ('Hack Shapes', 'hack.shapes'), | ||||
|       tuple ('Mixed Types', 'hack.annotations.mixedtypes'), | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   public function getDemoFilename(): string { | ||||
|     return 'demo.php'; | ||||
|   } | ||||
|  | ||||
|   public function getDemoResult(): string { | ||||
|     return db_result_main(); | ||||
|   } | ||||
|  | ||||
|   public function getDemoXHP(): ?:xhp { | ||||
|     return null; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										22
									
								
								samples/Hack/Documentation.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								samples/Hack/Documentation.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| <?hh // strict | ||||
| /** | ||||
|  * Copyright (c) 2014, Facebook, Inc. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * This source code is licensed under the BSD-style license found in the | ||||
|  * LICENSE file in the root directory of this source tree. An additional grant | ||||
|  * of patent rights can be found in the PATENTS file in the same directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| require_once $_SERVER['DOCUMENT_ROOT'].'/vendor/hhvm/xhp/src/init.php'; | ||||
|  | ||||
| final class :documentation extends :x:element { | ||||
|   attribute string name; | ||||
|  | ||||
|   protected function render(): :xhp { | ||||
|     $name = implode('.', explode(' ', $this->getAttribute('name'))).".php"; | ||||
|     $href = "http://hhvm.com/manual/en/$name"; | ||||
|     return <a class="docs button" href={$href} target="_blank">docs →</a>; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										65
									
								
								samples/Hack/FakeDB.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								samples/Hack/FakeDB.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| <?hh // strict | ||||
|  | ||||
| type DBResultExtra = shape('age' => int); | ||||
| type DBResult = shape( | ||||
|   'id' => int, | ||||
|   'name' => string, | ||||
|   'extra' => DBResultExtra, | ||||
| ); | ||||
|  | ||||
| final class FakeDB { | ||||
|   public function getRawRows(): array<array<string, mixed>> { | ||||
|     $good_extra = json_encode(array('age' => 40)); | ||||
|     $bad_extra = 'corrupt data'; | ||||
|     // Real code would query a DB, but for now let's hardcode it | ||||
|     return array( | ||||
|       array( | ||||
|         'id' => 123, | ||||
|         'name' => 'Alice', | ||||
|         'extra' => $good_extra, | ||||
|       ), | ||||
|       array( | ||||
|         'id' => 456, | ||||
|         'name' => 'Bob', | ||||
|         'extra' => $bad_extra, | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * When processing untyped data you need to check each piece of data and | ||||
|    * figure out whether to give up or recover when the data is bad | ||||
|    */ | ||||
|   public function processRow(array<string, mixed> $row): ?DBResult { | ||||
|     $row = Map::fromArray($row); | ||||
|     $id = $row->contains('id') ? $row['id'] : null; | ||||
|     $name = $row->contains('name') ? $row['name'] : null; | ||||
|     $extra = $row->contains('extra') ? json_decode($row['extra'], true) : null; | ||||
|  | ||||
|     // Ignore rows with invalid IDs or names | ||||
|     if (!is_int($id) || !is_string($name)) { | ||||
|       return null; | ||||
|     } | ||||
|  | ||||
|     // Try to recover from a bad extra column | ||||
|     if (!is_array($extra)) { | ||||
|       $extra = shape('age' => 0); | ||||
|     } else { | ||||
|       $extra = Map::fromArray($extra); | ||||
|       $extra = shape('age' => $extra->contains('age') ? $extra['age'] : 0); | ||||
|     } | ||||
|  | ||||
|     return shape('id' => $id, 'name' => $name, 'extra' => $extra); | ||||
|   } | ||||
|  | ||||
|   public function getDBResults(): Vector<DBResult> { | ||||
|     $ret = Vector {}; | ||||
|     foreach ($this->getRawRows() as $raw_row) { | ||||
|       $row = $this->processRow($raw_row); | ||||
|       if ($row !== null) { | ||||
|         $ret->add($row); | ||||
|       } | ||||
|     } | ||||
|     return $ret; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										72
									
								
								samples/Hack/GetAndPostRecipe.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								samples/Hack/GetAndPostRecipe.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | ||||
| <?hh // strict | ||||
| /** | ||||
|  * Copyright (c) 2014, Facebook, Inc. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * This source code is licensed under the BSD-style license found in the | ||||
|  * LICENSE file in the root directory of this source tree. An additional grant | ||||
|  * of patent rights can be found in the PATENTS file in the same directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| require_once $_SERVER['DOCUMENT_ROOT'].'/core/controller/recipe/init.php'; | ||||
| require_once "demo.php"; | ||||
|  | ||||
| class GetAndPostRecipe extends Recipe implements RecipeWithDemo { | ||||
|  | ||||
|   protected function getName(): string { | ||||
|     return '$_GET and $_POST'; | ||||
|   } | ||||
|  | ||||
|   <<Override>> | ||||
|   protected function getDescription(): ?string { | ||||
|     return 'A small example of how to interact with superglobals and the '. | ||||
|       'untyped data they can contain.'; | ||||
|   } | ||||
|  | ||||
|   protected function getFilenames(): Vector<string> { | ||||
|     return Vector { | ||||
|       'NonStrictFile.php', | ||||
|       'StrictFile.php', | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   protected function getDocs(): Vector<(string, string)> { | ||||
|     return Vector { | ||||
|       tuple('invariant()', 'hack.otherrulesandfeatures.invariant'), | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   public function getDemoFilename(): string { | ||||
|     return 'demo.php'; | ||||
|   } | ||||
|  | ||||
|   public function getDemoResult(): string { | ||||
|     return get_and_post_main(); | ||||
|   } | ||||
|  | ||||
|   public function getDemoXHP(): :xhp { | ||||
|     $url = '/recipes/get-and-post/'; | ||||
|     return | ||||
|       <x:frag> | ||||
|         <div> | ||||
|           <a href={"$url?myIntParam=8675309#demo"} class="button">GET myIntParam=8675309</a> | ||||
|         </div> | ||||
|         <div> | ||||
|         <a href={"$url?myIntParam=boom#demo"} class="button">GET myIntParam=boom</a> | ||||
|         </div> | ||||
|         <div> | ||||
|         <form action={"$url#demo"} method="post"> | ||||
|           <input type="hidden" name="myIntParam" value="5551234"/> | ||||
|           <input type="submit" class="button" value="POST myIntParam=5551234"/> | ||||
|         </form> | ||||
|         </div> | ||||
|         <div> | ||||
|         <form action={"$url#demo"} method="post"> | ||||
|           <input type="hidden" name="myIntParam" value="boom"/> | ||||
|           <input type="submit" class="button" value="POST myIntParam=boom"/> | ||||
|         </form> | ||||
|         </div> | ||||
|       </x:frag>; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										30
									
								
								samples/Hack/GetController.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								samples/Hack/GetController.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| <?hh // strict | ||||
| /** | ||||
|  * Copyright (c) 2014, Facebook, Inc. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * This source code is licensed under the BSD-style license found in the | ||||
|  * LICENSE file in the root directory of this source tree. An additional grant | ||||
|  * of patent rights can be found in the PATENTS file in the same directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| abstract class GetController extends Controller { | ||||
|   final protected function __construct(private Request $request) { | ||||
|     parent::__construct(); | ||||
|   } | ||||
|  | ||||
|   final protected function getRequest(): Request { | ||||
|     return $this->request; | ||||
|   } | ||||
|  | ||||
|   final public function go(array<mixed, mixed> $get): void { | ||||
|     $request = new Request(Map::fromArray($get)); | ||||
|     $controller = new static($request); | ||||
|     echo "<!DOCTYPE html>"; | ||||
|     $head = $controller->getHead(); | ||||
|     $body = $controller->render(); | ||||
|     echo (string)$head; | ||||
|     echo (string)$body; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										38
									
								
								samples/Hack/HomeController.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								samples/Hack/HomeController.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| <?hh // strict | ||||
| /** | ||||
|  * Copyright (c) 2014, Facebook, Inc. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * This source code is licensed under the BSD-style license found in the | ||||
|  * LICENSE file in the root directory of this source tree. An additional grant | ||||
|  * of patent rights can be found in the PATENTS file in the same directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| require_once $_SERVER['DOCUMENT_ROOT'].'/core/controller/init.php'; | ||||
| require_once $_SERVER['DOCUMENT_ROOT'].'/core/controller/standard-page/init.php'; | ||||
| require_once $_SERVER['DOCUMENT_ROOT'].'/vendor/hhvm/xhp/src/init.php'; | ||||
|  | ||||
| class HomeController extends GetController { | ||||
|   use StandardPage; | ||||
|  | ||||
|   protected function getTitle(): string { | ||||
|     return 'Hack Cookbook'; | ||||
|   } | ||||
|  | ||||
|   protected function renderMainColumn(): :xhp { | ||||
|     return <div> | ||||
|       <h1>Cookbook</h1> | ||||
|       <p> | ||||
|         The Hack Cookbook helps you write Hack code by giving you examples of | ||||
|         Hack code. It is written in Hack and is open source. If you | ||||
|         <a href="http://github.com/facebook/hack-example-site"> | ||||
|           head over to GitHub, | ||||
|         </a> | ||||
|         you can read the code, check out the repository, and run it | ||||
|         yourself. The recipes in this cookbook are small examples that | ||||
|         illustrate how to use Hack to solve common and interesting problems. | ||||
|       </p> | ||||
|     </div>; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										13
									
								
								samples/Hack/MySecureRequest.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								samples/Hack/MySecureRequest.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| <?hh // strict | ||||
|  | ||||
| require_once $_SERVER['DOCUMENT_ROOT'].'/core/funs/init.php'; | ||||
|  | ||||
| final class MySecureRequest { | ||||
|   public function __construct(private Map<string, mixed> $GETParams) {} | ||||
|   public function stringParam(string $name): UNESCAPED_STRING { | ||||
|     invariant($this->GETParams->contains($name), 'Unknown GET param: '.$name); | ||||
|     $raw_string = $this->GETParams[$name]; | ||||
|     invariant(is_string($raw_string), $name.' is not a string'); | ||||
|     return unescaped_string($raw_string); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										104
									
								
								samples/Hack/Nav.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								samples/Hack/Nav.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,104 @@ | ||||
| <?hh // strict | ||||
| /** | ||||
|  * Copyright (c) 2014, Facebook, Inc. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * This source code is licensed under the BSD-style license found in the | ||||
|  * LICENSE file in the root directory of this source tree. An additional grant | ||||
|  * of patent rights can be found in the PATENTS file in the same directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| require_once $_SERVER['DOCUMENT_ROOT'].'/vendor/hhvm/xhp/src/init.php'; | ||||
|  | ||||
| type NavItem = shape( | ||||
|   'name' => string, | ||||
|   'location' => string, | ||||
| ); | ||||
|  | ||||
| type NavSection = shape( | ||||
|   'name' => string, | ||||
|   'location' => ?string, | ||||
|   'items' => Vector<NavItem>, | ||||
| ); | ||||
|  | ||||
| final class :hack:nav extends :x:element { | ||||
|   private function getNavSections(): Vector<NavSection> { | ||||
|     return Vector{ | ||||
|       shape( | ||||
|         'name' => 'Home', | ||||
|         'location' => '/', | ||||
|         'items' => Vector {}, | ||||
|       ), | ||||
|       shape( | ||||
|         'name' => 'GitHub', | ||||
|         'location' => 'http://github.com/facebook/hack-example-site', | ||||
|         'items' => Vector {}, | ||||
|       ), | ||||
|       shape( | ||||
|         'name' => 'Recipes', | ||||
|         'location' => null, | ||||
|         'items' => Vector { | ||||
|           shape( | ||||
|             'name' => '$_GET and $_POST', | ||||
|             'location' => '/recipes/get-and-post/', | ||||
|           ), | ||||
|           shape( | ||||
|             'name' => 'Assert', | ||||
|             'location' => '/recipes/assert/', | ||||
|           ), | ||||
|           shape( | ||||
|             'name' => 'DB Result', | ||||
|             'location' => '/recipes/db-result/', | ||||
|           ), | ||||
|           shape( | ||||
|             'name' => 'Unescaped String', | ||||
|             'location' => '/recipes/unescaped-string/', | ||||
|           ), | ||||
|           shape( | ||||
|             'name' => 'User ID', | ||||
|             'location' => '/recipes/user-id/', | ||||
|           ), | ||||
|         }, | ||||
|       ), | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   private function renderNavItems(Vector<NavItem> $items): :xhp { | ||||
|     $render_item = $item ==> | ||||
|       <li> | ||||
|         <a class="navItem" href={$item['location']}> | ||||
|           {$item['name']} | ||||
|         </a> | ||||
|       </li>; | ||||
|     return | ||||
|       <x:frag> | ||||
|         {$items->map($render_item)->toArray()} | ||||
|       </x:frag>; | ||||
|   } | ||||
|  | ||||
|   private function renderNavSection(NavSection $section): :xhp { | ||||
|     $section_item = <h3 class="navItem">{$section['name']}</h3>; | ||||
|     if ($section['location'] !== null) { | ||||
|       $section_item = <a href={$section['location']}>{$section_item}</a>; | ||||
|     } | ||||
|     return | ||||
|       <li class="navSectionItem"> | ||||
|         {$section_item} | ||||
|         <ul class="navItems"> | ||||
|           {$this->renderNavItems($section['items'])} | ||||
|         </ul> | ||||
|       </li>; | ||||
|   } | ||||
|  | ||||
|   public function render(): :xhp { | ||||
|     $sections = $this->getNavSections() | ||||
|       ->map($section ==> $this->renderNavSection($section)); | ||||
|     return | ||||
|       <div class="nav"> | ||||
|         <ul class="navSections"> | ||||
|           {$sections->toArray()} | ||||
|         </ul> | ||||
|       </div>; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										27
									
								
								samples/Hack/NonStrictFile.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								samples/Hack/NonStrictFile.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| <?hh | ||||
| /** | ||||
|  * Copyright (c) 2014, Facebook, Inc. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * This source code is licensed under the BSD-style license found in the | ||||
|  * LICENSE file in the root directory of this source tree. An additional grant | ||||
|  * of patent rights can be found in the PATENTS file in the same directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| function getGETParams(): Map<string, mixed> { | ||||
|   // $_GET is not defined in code so Hack doesn't know about it and you can't | ||||
|   // use it in strict mode. You can interact with it outside of strict mode, | ||||
|   // though. | ||||
|   return Map::fromArray($_GET); | ||||
| } | ||||
|  | ||||
| function getPOSTParams(): Map<string, mixed> { | ||||
|   // Same deal with $_POST and other magically defined globals | ||||
|   return Map::fromArray($_POST); | ||||
| } | ||||
|  | ||||
| // Same deal with $_SERVER | ||||
| function isGET(): bool { | ||||
|   return $_SERVER['REQUEST_METHOD'] === 'GET'; | ||||
| } | ||||
							
								
								
									
										93
									
								
								samples/Hack/Recipe.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								samples/Hack/Recipe.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | ||||
| <?hh // strict | ||||
| /** | ||||
|  * Copyright (c) 2014, Facebook, Inc. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * This source code is licensed under the BSD-style license found in the | ||||
|  * LICENSE file in the root directory of this source tree. An additional grant | ||||
|  * of patent rights can be found in the PATENTS file in the same directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| require_once $_SERVER['DOCUMENT_ROOT'].'/core/controller/init.php'; | ||||
| require_once $_SERVER['DOCUMENT_ROOT'].'/core/controller/standard-page/init.php'; | ||||
| require_once $_SERVER['DOCUMENT_ROOT'].'/core/myxhp/init.php'; | ||||
|  | ||||
| abstract class Recipe extends GetController { | ||||
|   use StandardPage; | ||||
|  | ||||
|   abstract protected function getName(): string; | ||||
|   abstract protected function getFilenames(): Vector<string>; | ||||
|   abstract protected function getDocs(): Vector<(string, string)>; | ||||
|  | ||||
|   protected function getDescription(): ?string { | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|   final protected function getTitle(): string { | ||||
|     return $this->getName().' - Hack Cookbook'; | ||||
|   } | ||||
|  | ||||
|   final protected function renderMainColumn(): :xhp { | ||||
|     $main_column = | ||||
|       <x:frag> | ||||
|         <h1>{$this->getName()}</h1> | ||||
|       </x:frag>; | ||||
|     $description = $this->getDescription(); | ||||
|     if ($description !== null) { | ||||
|       $main_column->appendChild(<p>{$description}</p>); | ||||
|     } | ||||
|     foreach ($this->getFilenames() as $filename) { | ||||
|       $file = | ||||
|         <div class="file"> | ||||
|           <div class="filename">{$filename}</div> | ||||
|           <phpfile filename={$filename}/> | ||||
|         </div>; | ||||
|       $main_column->appendChild($file); | ||||
|     } | ||||
|     $recipe = $this; | ||||
|     if ($recipe instanceof RecipeWithDemo) { | ||||
|       try { | ||||
|         $result = $recipe->getDemoResult(); | ||||
|       } catch (Exception $e) { | ||||
|         $result = sprintf( | ||||
|           "Demo threw an %s:\n%s", | ||||
|           get_class($e), | ||||
|           $e->getMessage(), | ||||
|         ); | ||||
|       } | ||||
|       $result = explode("\n", trim($result)); | ||||
|       $result = array_map($x ==> <x:frag>{$x}<br/></x:frag>, $result); | ||||
|       $demo = | ||||
|         <x:frag> | ||||
|           <div class="demo" id="demo"> | ||||
|             <h3>Demo</h3> | ||||
|             {$recipe->getDemoXHP()} | ||||
|             <div class="filename">{$recipe->getDemoFilename()}</div> | ||||
|             <phpfile filename={$recipe->getDemoFilename()}/> | ||||
|             <div class="filename">Output</div> | ||||
|             <div class="demoResult"> | ||||
|               {$result} | ||||
|             </div> | ||||
|           </div> | ||||
|         </x:frag>; | ||||
|       $main_column->appendChild($demo); | ||||
|     } | ||||
|     if (!$this->getDocs()->isEmpty()) { | ||||
|       $render_doc_link = function($doc) { | ||||
|         list($name, $link) = $doc; | ||||
|         $link = "http://hhvm.com/manual/en/$link.php"; | ||||
|         return <li><a href={$link}>{$name}</a></li>; | ||||
|       }; | ||||
|       $main_column->appendChild( | ||||
|         <div class="docs"> | ||||
|           <h3>Relevant Official Documentation</h3> | ||||
|           <ul> | ||||
|             {$this->getDocs()->map($render_doc_link)->toArray()} | ||||
|           </ul> | ||||
|         </div> | ||||
|       ); | ||||
|     } | ||||
|     return $main_column; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										16
									
								
								samples/Hack/RecipeWithDemo.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								samples/Hack/RecipeWithDemo.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| <?hh // strict | ||||
| /** | ||||
|  * Copyright (c) 2014, Facebook, Inc. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * This source code is licensed under the BSD-style license found in the | ||||
|  * LICENSE file in the root directory of this source tree. An additional grant | ||||
|  * of patent rights can be found in the PATENTS file in the same directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| interface RecipeWithDemo { | ||||
|   public function getDemoFilename(): string; | ||||
|   public function getDemoResult(): string; | ||||
|   public function getDemoXHP(): ?:xhp; | ||||
| } | ||||
							
								
								
									
										15
									
								
								samples/Hack/Request.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								samples/Hack/Request.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| <?hh // strict | ||||
| /** | ||||
|  * Copyright (c) 2014, Facebook, Inc. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * This source code is licensed under the BSD-style license found in the | ||||
|  * LICENSE file in the root directory of this source tree. An additional grant | ||||
|  * of patent rights can be found in the PATENTS file in the same directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| final class Request { | ||||
|   public function __construct(private Map<string, mixed> $params) {} | ||||
| } | ||||
|  | ||||
							
								
								
									
										81
									
								
								samples/Hack/StandardPage.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								samples/Hack/StandardPage.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | ||||
| <?hh // strict | ||||
| /** | ||||
|  * Copyright (c) 2014, Facebook, Inc. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * This source code is licensed under the BSD-style license found in the | ||||
|  * LICENSE file in the root directory of this source tree. An additional grant | ||||
|  * of patent rights can be found in the PATENTS file in the same directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| trait StandardPage { | ||||
|   require extends Controller; | ||||
|  | ||||
|   abstract protected function renderMainColumn(): :xhp; | ||||
|  | ||||
|   protected function getExtraCSS(): Set<string> { | ||||
|     return Set {}; | ||||
|   } | ||||
|  | ||||
|   protected function getExtraJS(): Set<string> { | ||||
|     return Set {}; | ||||
|   } | ||||
|  | ||||
|   final protected function getCSS(): Set<string> { | ||||
|     return (Set { | ||||
|       '/css/base.css', | ||||
|     })->addAll($this->getExtraCSS()); | ||||
|   } | ||||
|  | ||||
|   final protected function getJS(): Set<string> { | ||||
|     return (Set { | ||||
|     })->addAll($this->getExtraJS()); | ||||
|   } | ||||
|  | ||||
|   final private function renderHeader(): :xhp { | ||||
|     return | ||||
|       <div class="hackHeader"> | ||||
|         <div class="width"> | ||||
|           <a href="http://hacklang.org/"> | ||||
|             <div class="logo">Hack</div> | ||||
|           </a> | ||||
|           <div class="headerNav"> | ||||
|           <ul> | ||||
|             <li> | ||||
|               <a href="http://hacklang.org/install/">Install</a> | ||||
|             </li> | ||||
|             <li> | ||||
|               <a href="http://hacklang.org/tutorial/">Tutorial</a> | ||||
|             </li> | ||||
|             <li> | ||||
|               <a href="/">Cookbook</a> | ||||
|             </li> | ||||
|             <li> | ||||
|               <a href="http://hhvm.com/manual">Docs</a> | ||||
|             </li> | ||||
|             <li> | ||||
|               <a href="http://github.com/facebook/hhvm">GitHub</a> | ||||
|             </li> | ||||
|             <li> | ||||
|               <a href="http://hhvm.com/">HHVM</a> | ||||
|             </li> | ||||
|           </ul> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div>; | ||||
|   } | ||||
|  | ||||
|   final protected function render(): :xhp { | ||||
|     return | ||||
|       <div> | ||||
|         {$this->renderHeader()} | ||||
|         <div class="width"> | ||||
|           <div class="mainContainer"> | ||||
|             <div class="mainColumn">{$this->renderMainColumn()}</div> | ||||
|             <hack:nav/> | ||||
|           </div> | ||||
|         </div> | ||||
|     </div>; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										46
									
								
								samples/Hack/StrictFile.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								samples/Hack/StrictFile.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| <?hh // strict | ||||
| /** | ||||
|  * Copyright (c) 2014, Facebook, Inc. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * This source code is licensed under the BSD-style license found in the | ||||
|  * LICENSE file in the root directory of this source tree. An additional grant | ||||
|  * of patent rights can be found in the PATENTS file in the same directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| require_once $_SERVER['DOCUMENT_ROOT'].'/core/funs/init.php'; | ||||
|  | ||||
| abstract class MyRequest { | ||||
|   abstract public function getParams(): Map<string, mixed>; | ||||
|  | ||||
|   // Throws when things go wrong | ||||
|   public function intParamX(string $name): int { | ||||
|     $params = $this->getParams(); | ||||
|     invariant($params->contains($name), sprintf('Unknown param: %s', $name)); | ||||
|     $param = $params[$name]; | ||||
|     invariant(is_numeric($param), sprintf('Param %s is not an int', $name)); | ||||
|     return (int)$param; | ||||
|   } | ||||
|  | ||||
|   // A lenient version | ||||
|   public function intParam(string $name): ?int { | ||||
|     $params = $this->getParams(); | ||||
|     if (!$params->contains($name)) { return null; } | ||||
|     $param = $params[$name]; | ||||
|     if (!is_numeric($param)) { return null; } | ||||
|     return (int)$param; | ||||
|   } | ||||
| } | ||||
|  | ||||
| final class MyGETRequest extends MyRequest { | ||||
|   public function getParams(): Map<string, mixed> { | ||||
|     return getGETParams(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| final class MyPOSTRequest extends MyRequest { | ||||
|   public function getParams(): Map<string, mixed> { | ||||
|     return getPOSTParams(); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										16
									
								
								samples/Hack/UnescapedString.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								samples/Hack/UnescapedString.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| <?hh // strict | ||||
|  | ||||
| // Outside of this file, no one knows that UNESCAPED_STRING is a string | ||||
| newtype UNESCAPED_STRING = string; | ||||
|  | ||||
| // This is how we initially taint a string. | ||||
| function unescaped_string(string $s): UNESCAPED_STRING { | ||||
|   return $s; | ||||
| } | ||||
|  | ||||
| // This is the only thing you can do with an UNESCAPED_STRING (other than pass | ||||
| // it around) | ||||
| function escape_unescaped_string(UNESCAPED_STRING $s): string { | ||||
|   // Your use case will decide how you want to escape your strings | ||||
|   return sprintf('Escaped ---> "%s" <--- Escaped', $s); | ||||
| } | ||||
							
								
								
									
										59
									
								
								samples/Hack/UnescapedStringRecipe.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								samples/Hack/UnescapedStringRecipe.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| <?hh // strict | ||||
| /** | ||||
|  * Copyright (c) 2014, Facebook, Inc. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * This source code is licensed under the BSD-style license found in the | ||||
|  * LICENSE file in the root directory of this source tree. An additional grant | ||||
|  * of patent rights can be found in the PATENTS file in the same directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| require_once $_SERVER['DOCUMENT_ROOT'].'/core/controller/recipe/init.php'; | ||||
| require_once "demo.php"; | ||||
|  | ||||
| class UnescapedStringRecipe extends Recipe implements RecipeWithDemo { | ||||
|  | ||||
|   protected function getName(): string { | ||||
|     return 'Unescaped string'; | ||||
|   } | ||||
|  | ||||
|   <<Override>> | ||||
|   protected function getDescription(): ?string { | ||||
|     return 'Forgetting to properly escape the strings you get from your users '. | ||||
|       'can lead to serious security holes. Hack can help by forcing you to '. | ||||
|       'escape these strings before using them as strings.'; | ||||
|   } | ||||
|  | ||||
|   protected function getFilenames(): Vector<string> { | ||||
|     return Vector { | ||||
|       'UnescapedString.php', | ||||
|       'MySecureRequest.php', | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   protected function getDocs(): Vector<(string, string)> { | ||||
|     return Vector{ | ||||
|       tuple('Opaque Type Aliasing',  'hack.typealiasing.opaquetypealiasing'), | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   public function getDemoFilename(): string { | ||||
|     return 'demo.php'; | ||||
|   } | ||||
|  | ||||
|   public function getDemoResult(): string { | ||||
|     return unescaped_string_main(); | ||||
|   } | ||||
|  | ||||
|   public function getDemoXHP(): ?:xhp { | ||||
|     $url = '/recipes/unescaped-string/'; | ||||
|     return | ||||
|       <x:frag> | ||||
|         Try setting the myStrParam GET param to something nice and innocent with this button... | ||||
|         <div> | ||||
|           <a href={"$url?myStrParam='); DROP TABLE important_stuff; --#demo"} class="button">GET myStrParam=Hello world</a> | ||||
|         </div> | ||||
|       </x:frag>; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										33
									
								
								samples/Hack/UserID.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								samples/Hack/UserID.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| <?hh // strict | ||||
| /** | ||||
|  * Copyright (c) 2014, Facebook, Inc. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * This source code is licensed under the BSD-style license found in the | ||||
|  * LICENSE file in the root directory of this source tree. An additional grant | ||||
|  * of patent rights can be found in the PATENTS file in the same directory. | ||||
|  * | ||||
|  */ | ||||
| require_once $_SERVER['DOCUMENT_ROOT'].'/core/funs/init.php'; | ||||
|  | ||||
| // Outside of this file, no one knows that these types are ints. They do know | ||||
| // that USER_ID is an ID and COW_ID is an ID | ||||
| newtype ID = int; | ||||
| newtype USER_ID as ID = ID; | ||||
| newtype COW_ID as ID = ID; | ||||
|  | ||||
| function assert_user_id(int $x): USER_ID { | ||||
|   // Everyone knows all user ids are odd | ||||
|   invariant($x % 2, sprintf('Invalid user ID: %d', $x)); | ||||
|   return $x; | ||||
| } | ||||
|  | ||||
| function assert_cow_id(int $x): COW_ID { | ||||
|   // Everyone knows all cow ids are even | ||||
|   invariant($x % 2 === 0, sprintf('Invalid cow ID: %d', $x)); | ||||
|   return $x; | ||||
| } | ||||
|  | ||||
| function id_to_int(ID $id): int { | ||||
|   return $id; | ||||
| } | ||||
							
								
								
									
										54
									
								
								samples/Hack/UserIDRecipe.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								samples/Hack/UserIDRecipe.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| <?hh // strict | ||||
| /** | ||||
|  * Copyright (c) 2014, Facebook, Inc. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * This source code is licensed under the BSD-style license found in the | ||||
|  * LICENSE file in the root directory of this source tree. An additional grant | ||||
|  * of patent rights can be found in the PATENTS file in the same directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| require_once $_SERVER['DOCUMENT_ROOT'].'/core/controller/recipe/init.php'; | ||||
| require_once "demo.php"; | ||||
|  | ||||
| class UserIDRecipe extends Recipe implements RecipeWithDemo { | ||||
|  | ||||
|   protected function getName(): string { | ||||
|     return 'User ID'; | ||||
|   } | ||||
|  | ||||
|   <<Override>> | ||||
|   protected function getDescription(): ?string { | ||||
|     return 'Protect your user IDs from being confused with normal ints'; | ||||
|   } | ||||
|  | ||||
|   protected function getFilenames(): Vector<string> { | ||||
|     return Vector { | ||||
|       'UserID.php', | ||||
|       'UsingUserID.php', | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   protected function getDocs(): Vector<(string, string)> { | ||||
|     return Vector { | ||||
|       tuple('Opaque Type Aliasing',  'hack.typealiasing.opaquetypealiasing'), | ||||
|       tuple( | ||||
|         'Opaque Type Aliasing with Constraints', | ||||
|         'hack.typealiasing.opaquewithconstraints', | ||||
|       ), | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   public function getDemoFilename(): string { | ||||
|     return 'demo.php'; | ||||
|   } | ||||
|  | ||||
|   public function getDemoResult(): string { | ||||
|     return user_id_main(); | ||||
|   } | ||||
|  | ||||
|   public function getDemoXHP(): ?:xhp { | ||||
|     return null; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										22
									
								
								samples/Hack/UsingUserID.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								samples/Hack/UsingUserID.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| <?hh // strict | ||||
| /** | ||||
|  * Copyright (c) 2014, Facebook, Inc. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * This source code is licensed under the BSD-style license found in the | ||||
|  * LICENSE file in the root directory of this source tree. An additional grant | ||||
|  * of patent rights can be found in the PATENTS file in the same directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| function get_something_string(ID $id, string $something): string { | ||||
|   return sprintf("Awesome %s #%d\n", $something, id_to_int($id)); | ||||
| } | ||||
|  | ||||
| function get_user_string(USER_ID $id): string { | ||||
|   return get_something_string($id, 'user'); | ||||
| } | ||||
|  | ||||
| function get_cow_string(COW_ID $id): string { | ||||
|   return get_something_string($id, 'cow'); | ||||
| } | ||||
							
								
								
									
										43
									
								
								samples/Hack/error.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								samples/Hack/error.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| <?hh | ||||
| /** | ||||
|  * Copyright (c) 2014, Facebook, Inc. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * This source code is licensed under the BSD-style license found in the | ||||
|  * LICENSE file in the root directory of this source tree. An additional grant | ||||
|  * of patent rights can be found in the PATENTS file in the same directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| final class TypehintViolationException extends Exception { | ||||
| } | ||||
|  | ||||
| function setup_errors(): void { | ||||
|   set_error_handler('handle_error', E_ALL); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * I want to turn failed typehints into exceptions so that I can handle them in | ||||
|  * my example code | ||||
|  */ | ||||
| function handle_error( | ||||
|   $errno, | ||||
|   $errstr, | ||||
|   $errfile, | ||||
|   $errline, | ||||
|   $errcontext = array(), | ||||
|   $errtrace = array(), | ||||
| ): bool { | ||||
|   if (E_RECOVERABLE_ERROR == $errno) { | ||||
|     // Transform typehint failures into an exception. | ||||
|     if (strpos($errstr, 'must be an instance of ') !== false) { | ||||
|       throw new TypehintViolationException($errstr); | ||||
|     } | ||||
|     // Transform nullable type violations to exceptions. | ||||
|     if ((strpos($errstr, 'must be of type ?') !== false) && | ||||
|         (strpos($errstr, 'Value returned from') === false)) { | ||||
|       throw new TypehintViolationException($errstr); | ||||
|     } | ||||
|   } | ||||
|   return false; | ||||
| } | ||||
							
								
								
									
										32
									
								
								samples/Hack/funs.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								samples/Hack/funs.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| <?hh | ||||
| /** | ||||
|  * Copyright (c) 2014, Facebook, Inc. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * This source code is licensed under the BSD-style license found in the | ||||
|  * LICENSE file in the root directory of this source tree. An additional grant | ||||
|  * of patent rights can be found in the PATENTS file in the same directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * This file contains a bunch of php stubs for functions that have been added | ||||
|  * to hhvm (though aren't in a release yet). These are important because the | ||||
|  * Hack typechecker can understand them | ||||
|  */ | ||||
|  | ||||
| class InvariantViolationException extends Exception {} | ||||
|  | ||||
| function invariant(mixed $test, string $message): void { | ||||
|   if (!$test) { | ||||
|     invariant_violation($message); | ||||
|   } | ||||
| } | ||||
|  | ||||
| function invariant_violation(string $message): void { | ||||
|   throw new InvariantViolationException($message); | ||||
| } | ||||
|  | ||||
| function class_meth(string $class, string $method) { | ||||
|   return array($class, $method); | ||||
| } | ||||
							
								
								
									
										32
									
								
								samples/Hack/funs.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								samples/Hack/funs.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| <?hh | ||||
| /** | ||||
|  * Copyright (c) 2014, Facebook, Inc. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * This source code is licensed under the BSD-style license found in the | ||||
|  * LICENSE file in the root directory of this source tree. An additional grant | ||||
|  * of patent rights can be found in the PATENTS file in the same directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * This file contains a bunch of php stubs for functions that have been added | ||||
|  * to hhvm (though aren't in a release yet). These are important because the | ||||
|  * Hack typechecker can understand them | ||||
|  */ | ||||
|  | ||||
| class InvariantViolationException extends Exception {} | ||||
|  | ||||
| function invariant(mixed $test, string $message): void { | ||||
|   if (!$test) { | ||||
|     invariant_violation($message); | ||||
|   } | ||||
| } | ||||
|  | ||||
| function invariant_violation(string $message): void { | ||||
|   throw new InvariantViolationException($message); | ||||
| } | ||||
|  | ||||
| function class_meth(string $class, string $method) { | ||||
|   return array($class, $method); | ||||
| } | ||||
							
								
								
									
										14
									
								
								samples/Hack/index.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								samples/Hack/index.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| <?hh | ||||
| /** | ||||
|  * Copyright (c) 2014, Facebook, Inc. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * This source code is licensed under the BSD-style license found in the | ||||
|  * LICENSE file in the root directory of this source tree. An additional grant | ||||
|  * of patent rights can be found in the PATENTS file in the same directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| require_once 'HomeController.php'; | ||||
|  | ||||
| HomeController::go($_GET); | ||||
							
								
								
									
										31
									
								
								samples/Hack/phpfile.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								samples/Hack/phpfile.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| <?hh // strict | ||||
| /** | ||||
|  * Copyright (c) 2014, Facebook, Inc. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * This source code is licensed under the BSD-style license found in the | ||||
|  * LICENSE file in the root directory of this source tree. An additional grant | ||||
|  * of patent rights can be found in the PATENTS file in the same directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| require_once $_SERVER['DOCUMENT_ROOT'].'/vendor/hhvm/xhp/src/init.php'; | ||||
|  | ||||
| final class :phpfile extends :x:primitive { | ||||
|   category %flow; | ||||
|  | ||||
|   attribute string filename; | ||||
|  | ||||
|   /** | ||||
|    * Ok, I'll admit this is kind of gross. I don't really want to implement | ||||
|    * syntax highlighting, so I'm relying on the built-in PHP support. XHP | ||||
|    * makes html strings sort of difficult to use (which is good cause they're | ||||
|    * dangerous). Anyway, this is one way around it :) | ||||
|    */ | ||||
|   protected function stringify(): string { | ||||
|     return | ||||
|       '<div class="code">'. | ||||
|       (string)highlight_file($this->getAttribute('filename'), /*ret*/ true). | ||||
|       '</div>'; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										14
									
								
								samples/Hack/startup.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								samples/Hack/startup.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| <?hh // strict | ||||
| /** | ||||
|  * Copyright (c) 2014, Facebook, Inc. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * This source code is licensed under the BSD-style license found in the | ||||
|  * LICENSE file in the root directory of this source tree. An additional grant | ||||
|  * of patent rights can be found in the PATENTS file in the same directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| function startup(): void { | ||||
|   setup_errors(); | ||||
| } | ||||
| @@ -105,6 +105,12 @@ class TestHeuristcs < Test::Unit::TestCase | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   def test_hack_by_heuristics | ||||
|     languages = ["Hack", "PHP"] | ||||
|     results = Heuristics.disambiguate_hack(fixture("Hack/funs.php"), languages) | ||||
|     assert_equal Language["Hack"], results.first | ||||
|   end | ||||
|  | ||||
|   def test_sc_supercollider_by_heuristics | ||||
|     languages = ["Scala", "SuperCollider"] | ||||
|     results = Heuristics.disambiguate_sc(fixture("SuperCollider/WarpPreset.sc"), languages) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user