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) } |         if languages.all? { |l| ["Common Lisp", "OpenCL"].include?(l) } | ||||||
|           result = disambiguate_cl(data, languages) |           result = disambiguate_cl(data, languages) | ||||||
|         end |         end | ||||||
|  |         if languages.all? { |l| ["Hack", "PHP"].include?(l) } | ||||||
|  |           result = disambiguate_hack(data, languages) | ||||||
|  |         end | ||||||
|         if languages.all? { |l| ["Scala", "SuperCollider"].include?(l) } |         if languages.all? { |l| ["Scala", "SuperCollider"].include?(l) } | ||||||
|           result = disambiguate_sc(data, languages) |           result = disambiguate_sc(data, languages) | ||||||
|         end |         end | ||||||
| @@ -94,6 +97,16 @@ module Linguist | |||||||
|       matches |       matches | ||||||
|     end |     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) |     def self.disambiguate_sc(data, languages) | ||||||
|       matches = [] |       matches = [] | ||||||
|       if (/\^(this|super)\./.match(data) || /^\s*(\+|\*)\s*\w+\s*{/.match(data) || /^\s*~\w+\s*=\./.match(data)) |       if (/\^(this|super)\./.match(data) || /^\s*(\+|\*)\s*\w+\s*{/.match(data) || /^\s*~\w+\s*=\./.match(data)) | ||||||
|   | |||||||
| @@ -1021,6 +1021,14 @@ HTTP: | |||||||
|   extensions: |   extensions: | ||||||
|   - .http |   - .http | ||||||
|  |  | ||||||
|  | Hack: | ||||||
|  |   type: programming | ||||||
|  |   lexer: PHP | ||||||
|  |   ace_mode: php | ||||||
|  |   extensions: | ||||||
|  |   - .hh | ||||||
|  |   - .php | ||||||
|  |  | ||||||
| Haml: | Haml: | ||||||
|   group: HTML |   group: HTML | ||||||
|   type: markup |   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 | ||||||
|   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 |   def test_sc_supercollider_by_heuristics | ||||||
|     languages = ["Scala", "SuperCollider"] |     languages = ["Scala", "SuperCollider"] | ||||||
|     results = Heuristics.disambiguate_sc(fixture("SuperCollider/WarpPreset.sc"), languages) |     results = Heuristics.disambiguate_sc(fixture("SuperCollider/WarpPreset.sc"), languages) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user