File: //usr/share/hhvm/hack/hacklib/hacklib.php
<?php
/**
* 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 "hack" directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
const SEP = DIRECTORY_SEPARATOR;
require_once(__DIR__.SEP.'containers'.SEP.'interfaces.php');
require_once(__DIR__.SEP.'containers'.SEP.'collections.ns.php');
require_once(__DIR__.SEP.'containers'.SEP.'vector.php');
require_once(__DIR__.SEP.'containers'.SEP.'immVector.php');
require_once(__DIR__.SEP.'containers'.SEP.'pair.php');
require_once(__DIR__.SEP.'containers'.SEP.'map.php');
require_once(__DIR__.SEP.'containers'.SEP.'immMap.php');
require_once(__DIR__.SEP.'containers'.SEP.'set.php');
require_once(__DIR__.SEP.'containers'.SEP.'immSet.php');
require_once(__DIR__.SEP.'hacklib_enum_like.php');
require_once(__DIR__.SEP.'invariant.ns.php');
class InvalidOperationException extends RuntimeException {}
// This invalid UTF-8 subsequence prevents users from entering this string
// to prevent accidental collisions.
const HACKLIB_UNINIT = "<<hacklib-uninitialized:\xEE\xFF\xFF>>";
/**
* Identity function. Mostly useful idiomatically, because this doesn't
* compile:
*
* new ClassName()->methodName();
*
* ...but this works fine:
*
* id(new ClassName())->method();
*
* @param Value to return.
* @return The passed value.
*/
function hacklib_id($x) {
return $x;
}
/**
* Abstracts patterns similar to:
* $r = null;
* if ($x) {
* $r = $x->y();
* }
* or
* if ($v) {
* $v->doSomething();
* }
*
* to be
* $r = nullsafe($x)->y();
* or
* nullsafe($v)->doSomething();
*
* Another way to think of it is a kind of idx() for objects.
*/
function hacklib_nullsafe($v) {
if ($v === null) {
return HACKLIB_NullObject::getInstance();
} else {
return $v;
}
}
/**
* A lot of behaviour needs to be specialcased to collections.
* none of the interfaces that exist are safe to use since
* technically any user code could implement them and the lazyiterables
* in hack do not follow the same rules.
*/
function hacklib_is_collection($v) {
static $accepted_types = array(
"HH\Pair" => true,
"HH\Vector" => true,
"HH\ImmVector" => true,
"HH\Set" => true,
"HH\ImmSet" => true,
"HH\Map" => true,
"HH\ImmMap" => true);
return is_object($v) && array_key_exists(get_class($v), $accepted_types);
}
/**
* When a hack collection is cast to array, this calls toArray
*/
function hacklib_cast_as_array($c) {
return hacklib_is_collection($c) ? $c->toArray() : $c;
}
/**
* When a hack collection is cast to boolean, this checks toEmpty
*/
function hacklib_cast_as_boolean($c) {
return hacklib_is_collection($c) ? !($c->isEmpty()) : $c;
}
/**
* Specialcasing of equality for hack collections.
*/
function hacklib_equals($o1, $o2) {
if (hacklib_is_collection($o1)) {
return $o1->hacklib_equals($o2);
}
if (hacklib_is_collection($o2)) {
return $o2->hacklib_equals($o1);
}
return $o1 == $o2;
}
/**
* Specialcasing of inequality for hack collections.
*/
function hacklib_not_equals($o1, $o2) {
if ($o1 instanceof ConstCollection) {
return !$o1->hacklib_equals($o2);
}
if ($o2 instanceof ConstCollection) {
return !$o2->hacklib_equals($o1);
}
return $o1 != $o2;
}
/**
* instanceof has to make adjustments for the fact that
* arrays are Traversable
*/
function hacklib_instanceof($e, $instStr) {
if ($instStr === 'HH\Traversable' && is_array($e)) {
return true;
}
return $e instanceof $instStr;
}
final class HACKLIB_NullObject {
private static $instance = null;
public static function getInstance() {
if (self::$instance) {
return self::$instance;
}
return self::$instance = new HACKLIB_NullObject();
}
private function __construct() {}
public function __call($method, array $arguments) {
return null;
}
}