2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source path is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the path LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
25 #include <string_view>
26 #include <sys/types.h>
28 #include <folly/Hash.h>
29 #include <folly/concurrency/ConcurrentHashMap.h>
30 #include <folly/executors/GlobalExecutor.h>
31 #include <folly/experimental/io/FsUtil.h>
32 #include <folly/io/async/EventBaseThread.h>
33 #include <folly/json.h>
35 #include <watchman/cppclient/WatchmanClient.h>
37 #include "hphp/runtime/base/autoload-handler.h"
38 #include "hphp/runtime/base/autoload-map.h"
39 #include "hphp/runtime/base/builtin-functions.h"
40 #include "hphp/runtime/base/config.h"
41 #include "hphp/runtime/base/init-fini-node.h"
42 #include "hphp/runtime/base/static-string-table.h"
43 #include "hphp/runtime/base/string-data.h"
44 #include "hphp/runtime/base/type-string.h"
45 #include "hphp/runtime/base/variable-serializer.h"
46 #include "hphp/runtime/ext/extension.h"
47 #include "hphp/runtime/ext/facts/autoload-db.h"
48 #include "hphp/runtime/ext/facts/ext_facts.h"
49 #include "hphp/runtime/ext/facts/string-ptr.h"
50 #include "hphp/runtime/ext/facts/watchman-autoload-map.h"
51 #include "hphp/runtime/ext/facts/watchman.h"
52 #include "hphp/runtime/vm/treadmill.h"
53 #include "hphp/util/build-info.h"
54 #include "hphp/util/hash-map.h"
55 #include "hphp/util/hash.h"
56 #include "hphp/util/user-info.h"
57 #include "hphp/zend/zend-string.h"
65 constexpr std::string_view kEUIDPlaceholder
= "%{euid}";
66 constexpr std::string_view kSchemaPlaceholder
= "%{schema}";
67 constexpr std::chrono::seconds kDefaultExpirationTime
{30 * 60};
68 constexpr size_t kDBVersion
= 3;
70 struct RepoOptionsParseExc
: public std::runtime_error
{
71 explicit RepoOptionsParseExc(std::string msg
)
72 : std::runtime_error
{std::move(msg
)} {
77 * Get the directory containing the given RepoOptions file. We define this to
78 * be the root of the repository we're autoloading.
80 folly::fs::path
getRepoRoot(const RepoOptions
& options
) {
81 return folly::fs::canonical(folly::fs::path
{options
.path()}.parent_path());
85 getDBPath(const folly::fs::path
& root
, const folly::dynamic
& queryExpr
) {
86 std::stringstream schemaHash
;
87 schemaHash
<< std::hex
88 << folly::hash::hash_combine(
91 std::hash
<std::string_view
>{}(root
.native()));
93 std::string pathTemplate
{RuntimeOption::AutoloadDBPath
};
96 size_t idx
= pathTemplate
.find(kEUIDPlaceholder
);
97 if (idx
!= std::string::npos
) {
99 idx
, kEUIDPlaceholder
.size(), folly::to
<std::string
>(geteuid()));
104 size_t idx
= pathTemplate
.find(kSchemaPlaceholder
);
105 if (idx
!= std::string::npos
) {
106 pathTemplate
.replace(
107 idx
, kSchemaPlaceholder
.size(), std::move(schemaHash
).str());
111 folly::fs::path dbPath
= pathTemplate
;
112 if (dbPath
.is_relative()) {
113 dbPath
= root
/ dbPath
;
116 return folly::fs::system_complete(dbPath
);
120 // Resolve the group to a unix gid
121 if (RuntimeOption::AutoloadDBGroup
.empty()) {
125 GroupInfo grp
{RuntimeOption::AutoloadDBGroup
.c_str()};
126 return grp
.gr
->gr_gid
;
127 } catch (const Exception
& e
) {
128 Logger::Warning(folly::sformat(
129 "Can't resolve {} to a gid: {}",
130 RuntimeOption::AutoloadDBGroup
,
136 ::mode_t
getDBPerms() {
138 ::mode_t res
= std::stoi(RuntimeOption::AutoloadDBPerms
, 0, 8);
139 FTRACE(3, "Converted {} to {}\n", RuntimeOption::AutoloadDBPerms
, res
);
141 } catch (const std::exception
& e
) {
142 Logger::Warning(folly::sformat(
143 "Error running std::stoi on \"Autoload.DB.Perms\": {}", e
.what()));
149 const folly::fs::path
& root
,
150 const folly::dynamic
& queryExpr
,
151 const RepoOptions
& repoOptions
) {
152 assertx(root
.is_absolute());
154 auto trustedDBPath
= [&]() -> folly::fs::path
{
155 folly::fs::path trusted
{repoOptions
.trustedDBPath()};
156 if (trusted
.empty()) {
159 // If the trustedDBPath is relative, make sure we resolve it relative
160 // to the repo root rather than the current working directory
161 if (trusted
.is_relative()) {
162 trusted
= root
/ trusted
;
165 return folly::fs::canonical(trusted
);
166 } catch (const folly::fs::filesystem_error
& e
) {
167 throw RepoOptionsParseExc
{folly::sformat(
168 "Error resolving Autoload.TrustedDBPath = {}: {}",
169 trusted
.native().c_str(),
174 if (trustedDBPath
.empty()) {
175 ::gid_t gid
= getGroup();
176 return DBData::readWrite(getDBPath(root
, queryExpr
), gid
, getDBPerms());
178 return DBData::readOnly(std::move(trustedDBPath
));
182 // Convenience wrapper for std::string_view
183 inline strhash_t
hash_string_view_cs(std::string_view s
) {
184 return hash_string_cs(s
.data(), s
.size());
188 * List of options making a WatchmanAutoloadMap unique
190 struct WatchmanAutoloadMapKey
{
192 static WatchmanAutoloadMapKey
get(const RepoOptions
& repoOptions
) {
193 auto root
= getRepoRoot(repoOptions
);
195 auto queryExpr
= [&]() -> folly::dynamic
{
196 auto queryStr
= repoOptions
.autoloadQuery();
197 if (queryStr
.empty()) {
201 return folly::parseJson(queryStr
);
202 } catch (const folly::json::parse_error
& e
) {
203 throw RepoOptionsParseExc
{folly::sformat(
204 "Error JSON-parsing Autoload.Query = \"{}\": {}",
210 auto dbData
= getDBData(root
, queryExpr
, repoOptions
);
212 return WatchmanAutoloadMapKey
{
213 .m_root
= std::move(root
),
214 .m_queryExpr
= std::move(queryExpr
),
215 .m_dbData
= std::move(dbData
)};
218 bool operator==(const WatchmanAutoloadMapKey
& rhs
) const noexcept
{
219 return m_root
== rhs
.m_root
&& m_queryExpr
== rhs
.m_queryExpr
&&
220 m_dbData
== rhs
.m_dbData
;
223 std::string
toString() const {
224 return folly::sformat(
225 "WatchmanAutoloadMapKey({}, {}, {})",
227 folly::toJson(m_queryExpr
),
228 m_dbData
.toString());
231 strhash_t
hash() const noexcept
{
232 return folly::hash::hash_combine(
233 hash_string_view_cs(m_root
.native()),
239 * A repo is autoloadable if we can either:
241 * 1. Use Watchman to track the files and create our own database
242 * 2. Read an existing database file somewhere
244 bool isAutoloadableRepo() const {
245 return m_queryExpr
.isObject() ||
246 m_dbData
.m_rwMode
== SQLite::OpenMode::ReadOnly
;
249 folly::fs::path m_root
;
250 folly::dynamic m_queryExpr
;
259 template <> struct hash
<HPHP::Facts::WatchmanAutoloadMapKey
> {
260 size_t operator()(const HPHP::Facts::WatchmanAutoloadMapKey
& k
) const {
261 return static_cast<size_t>(k
.hash());
271 * Sent to AutoloadHandler so AutoloadHandler can create
272 * WatchmanAutoloadMaps across the open-source / FB-only boundary.
274 struct WatchmanAutoloadMapFactory final
: public FactsFactory
{
276 WatchmanAutoloadMapFactory() = default;
277 WatchmanAutoloadMapFactory(const WatchmanAutoloadMapFactory
&) = delete;
278 WatchmanAutoloadMapFactory(WatchmanAutoloadMapFactory
&&) = delete;
279 WatchmanAutoloadMapFactory
&
280 operator=(const WatchmanAutoloadMapFactory
&) = delete;
281 WatchmanAutoloadMapFactory
& operator=(WatchmanAutoloadMapFactory
&&) = delete;
282 ~WatchmanAutoloadMapFactory() override
= default;
284 FactsStore
* getForOptions(const RepoOptions
& options
) override
;
287 * Delete AutoloadMaps which haven't been accessed in the last
288 * `expirationTime` seconds.
290 void garbageCollectUnusedAutoloadMaps(std::chrono::seconds expirationTime
);
296 * Map from root to AutoloadMap
298 hphp_hash_map
<WatchmanAutoloadMapKey
, std::shared_ptr
<FactsStore
>> m_maps
;
301 * Map from root to time we last accessed the AutoloadMap
304 WatchmanAutoloadMapKey
,
305 std::chrono::time_point
<std::chrono::steady_clock
>>
309 struct Facts final
: Extension
{
310 Facts() : Extension("facts", "1.0") {
313 void moduleLoad(const IniSetting::Map
& ini
, Hdf config
) override
{
315 // Create all resources at a deterministic time to avoid SIOF
316 m_data
= FactsData
{};
318 // An AutoloadMap may be freed after this many seconds since its last use
319 m_data
->m_expirationTime
= std::chrono::seconds
{Config::GetInt64(
322 "Autoload.MapIdleGCTimeSeconds",
323 kDefaultExpirationTime
.count())};
324 if (m_data
->m_expirationTime
!= kDefaultExpirationTime
) {
327 "Autoload.MapIdleGCTimeSeconds = {}\n",
328 m_data
->m_expirationTime
.count());
331 auto watchmanSocket
=
332 Config::GetString(ini
, config
, "watchman.socket.default");
333 if (!watchmanSocket
.empty()) {
334 FTRACE(3, "watchman.socket.default = {}\n", watchmanSocket
);
335 m_data
->m_watchmanDefaultSocket
= std::move(watchmanSocket
);
338 auto watchmanRootSocket
=
339 Config::GetString(ini
, config
, "watchman.socket.root");
340 if (!watchmanRootSocket
.empty()) {
341 FTRACE(3, "watchman.socket.root = {}\n", watchmanRootSocket
);
342 m_data
->m_watchmanRootSocket
= std::move(watchmanRootSocket
);
345 auto excluded
= Config::GetStrVector(ini
, config
, "Autoload.ExcludedRepos");
346 for (auto const& repo
: excluded
) {
348 m_data
->m_excludedRepos
.insert(folly::fs::canonical(repo
).native());
349 } catch (const folly::fs::filesystem_error
& e
) {
351 "Could not disable native autoloader for %s: %s\n",
358 void moduleInit() override
{
359 HHVM_NAMED_FE(HH
\\Facts
\\enabled
, HHVM_FN(facts_enabled
));
360 HHVM_NAMED_FE(HH
\\Facts
\\db_path
, HHVM_FN(facts_db_path
));
361 HHVM_NAMED_FE(HH
\\Facts
\\type_to_path
, HHVM_FN(facts_type_to_path
));
362 HHVM_NAMED_FE(HH
\\Facts
\\function_to_path
, HHVM_FN(facts_function_to_path
));
363 HHVM_NAMED_FE(HH
\\Facts
\\constant_to_path
, HHVM_FN(facts_constant_to_path
));
365 HH
\\Facts
\\type_alias_to_path
, HHVM_FN(facts_type_alias_to_path
));
367 HHVM_NAMED_FE(HH
\\Facts
\\path_to_types
, HHVM_FN(facts_path_to_types
));
369 HH
\\Facts
\\path_to_functions
, HHVM_FN(facts_path_to_functions
));
371 HH
\\Facts
\\path_to_constants
, HHVM_FN(facts_path_to_constants
));
373 HH
\\Facts
\\path_to_type_aliases
, HHVM_FN(facts_path_to_type_aliases
));
374 HHVM_NAMED_FE(HH
\\Facts
\\type_name
, HHVM_FN(facts_type_name
));
375 HHVM_NAMED_FE(HH
\\Facts
\\kind
, HHVM_FN(facts_kind
));
376 HHVM_NAMED_FE(HH
\\Facts
\\is_abstract
, HHVM_FN(facts_is_abstract
));
377 HHVM_NAMED_FE(HH
\\Facts
\\is_final
, HHVM_FN(facts_is_final
));
378 HHVM_NAMED_FE(HH
\\Facts
\\subtypes
, HHVM_FN(facts_subtypes
));
380 HH
\\Facts
\\transitive_subtypes
, HHVM_FN(facts_transitive_subtypes
));
381 HHVM_NAMED_FE(HH
\\Facts
\\supertypes
, HHVM_FN(facts_supertypes
));
383 HH
\\Facts
\\types_with_attribute
, HHVM_FN(facts_types_with_attribute
));
385 HH
\\Facts
\\type_aliases_with_attribute
,
386 HHVM_FN(facts_type_aliases_with_attribute
));
387 HHVM_NAMED_FE(HH
\\Facts
\\type_attributes
, HHVM_FN(facts_type_attributes
));
389 HH
\\Facts
\\type_alias_attributes
, HHVM_FN(facts_type_alias_attributes
));
391 HH
\\Facts
\\type_attribute_parameters
,
392 HHVM_FN(facts_type_attribute_parameters
));
394 HH
\\Facts
\\type_alias_attribute_parameters
,
395 HHVM_FN(facts_type_alias_attribute_parameters
));
396 HHVM_NAMED_FE(HH
\\Facts
\\all_types
, HHVM_FN(facts_all_types
));
397 HHVM_NAMED_FE(HH
\\Facts
\\all_functions
, HHVM_FN(facts_all_functions
));
398 HHVM_NAMED_FE(HH
\\Facts
\\all_constants
, HHVM_FN(facts_all_constants
));
399 HHVM_NAMED_FE(HH
\\Facts
\\all_type_aliases
, HHVM_FN(facts_all_type_aliases
));
402 if (!RuntimeOption::AutoloadEnabled
) {
405 "Autoload.Enabled is not true, not enabling native "
410 if (RuntimeOption::AutoloadDBPath
.empty()) {
411 FTRACE(1, "Autoload.DB.Path was empty, not enabling native autoloader.\n");
415 if (!m_data
->m_watchmanDefaultSocket
) {
416 FTRACE(2, "watchman.socket.default was not provided.\n");
419 if (!m_data
->m_watchmanRootSocket
) {
420 FTRACE(2, "watchman.socket.root was not provided.\n");
423 m_data
->m_mapFactory
= std::make_unique
<WatchmanAutoloadMapFactory
>();
424 FactsFactory::setInstance(m_data
->m_mapFactory
.get());
427 void moduleShutdown() override
{
428 // Destroy all resources at a deterministic time to avoid SDOF
429 FactsFactory::setInstance(nullptr);
433 std::chrono::seconds
getExpirationTime() const {
434 return m_data
->m_expirationTime
;
437 const std::optional
<std::string
>& getWatchmanDefaultSocket() const {
438 return m_data
->m_watchmanDefaultSocket
;
441 const std::optional
<std::string
>& getWatchmanRootSocket() const {
442 return m_data
->m_watchmanRootSocket
;
445 Watchman
& getWatchmanClient(const folly::fs::path
& root
) {
446 auto it
= m_data
->m_watchmanClients
.find(root
.native());
447 if (it
!= m_data
->m_watchmanClients
.end()) {
450 auto watchmanSocket
= getPerUserWatchmanSocket();
451 if (!watchmanSocket
) {
452 FTRACE(3, "No per-user watchman socket found.\n");
453 watchmanSocket
= getWatchmanDefaultSocket();
456 return *m_data
->m_watchmanClients
457 .insert(root
.native(), Watchman::get(root
, watchmanSocket
))
462 // Add new members to this struct instead of the top level so we can be sure
463 // your new member is destroyed at the right time.
465 std::chrono::seconds m_expirationTime
{30 * 60};
466 std::optional
<std::string
> m_watchmanDefaultSocket
;
467 std::optional
<std::string
> m_watchmanRootSocket
;
468 hphp_hash_set
<std::string
> m_excludedRepos
;
469 folly::ConcurrentHashMap
<std::string
, std::shared_ptr
<Watchman
>>
471 std::unique_ptr
<WatchmanAutoloadMapFactory
> m_mapFactory
;
473 std::optional
<FactsData
> m_data
;
476 * Discover who owns the given repo and return the Watchman socket
477 * corresponding to that user.
479 std::optional
<std::string
> getPerUserWatchmanSocket() {
480 IniSetting::Map ini
= IniSetting::Map::object
;
482 auto* repoOptions
= g_context
->getRepoOptionsForRequest();
487 // Figure out who owns the repo we're trying to run code in
488 auto repoRoot
= getRepoRoot(*repoOptions
);
489 int repoRootFD
= ::open(repoRoot
.native().c_str(), O_DIRECTORY
| O_RDONLY
);
490 if (repoRootFD
== -1) {
496 struct ::stat hstat
{};
497 if (::fstat(repoRootFD
, &hstat
) != 0) {
501 // The repo is owned by root, so use a special root socket
502 if (hstat
.st_uid
== 0) {
503 auto const& rootSock
= getWatchmanRootSocket();
506 "{} is owned by root, looking for socket at {}\n",
508 rootSock
? *rootSock
: "<none>");
512 // Find the `watchman.socket` setting in the repo owner's
513 // SandboxConfFile (usually a .hphp file somewhere in their home
515 UserInfo info
{hstat
.st_uid
};
516 auto user
= std::string
{info
.pw
->pw_name
};
517 auto homePath
= RuntimeOption::GetHomePath(user
);
521 auto confFileName
= (*homePath
) / RuntimeOption::SandboxConfFile
;
523 RuntimeOption::ReadPerUserSettings(confFileName
, ini
, config
);
527 auto sock
= Config::GetString(ini
, config
, "watchman.socket.default");
531 return {std::move(sock
)};
537 WatchmanAutoloadMapFactory::getForOptions(const RepoOptions
& options
) {
539 auto mapKey
= [&]() -> std::optional
<WatchmanAutoloadMapKey
> {
541 auto mk
= WatchmanAutoloadMapKey::get(options
);
542 if (!mk
.isAutoloadableRepo()) {
545 return {std::move(mk
)};
546 } catch (const RepoOptionsParseExc
& e
) {
547 Logger::Warning("%s\n", e
.what());
556 std::unique_lock g
{m_mutex
};
558 // Mark the fact that we've accessed the map
559 m_lastUsed
.insert_or_assign(*mapKey
, std::chrono::steady_clock::now());
561 // Try to return a corresponding WatchmanAutoloadMap
562 auto const it
= m_maps
.find(*mapKey
);
563 if (it
!= m_maps
.end()) {
564 return it
->second
.get();
567 // We're creating a new map. This is a good sign that an existing map may
568 // be defunct, so schedule a cleanup job to check.
570 [this] { garbageCollectUnusedAutoloadMaps(s_ext
.getExpirationTime()); });
572 if (mapKey
->m_dbData
.m_rwMode
== SQLite::OpenMode::ReadOnly
) {
575 "Loading {} from trusted Autoload DB at {}\n",
576 mapKey
->m_root
.native(),
577 mapKey
->m_dbData
.m_path
.native());
581 std::make_shared
<WatchmanAutoloadMap
>(
582 mapKey
->m_root
, mapKey
->m_dbData
)})
583 .first
->second
.get();
586 assertx(mapKey
->m_queryExpr
.isObject());
587 auto map
= std::make_shared
<WatchmanAutoloadMap
>(
591 s_ext
.getWatchmanClient(mapKey
->m_root
));
593 if (RuntimeOption::ServerExecutionMode()) {
597 return m_maps
.insert({*mapKey
, std::move(map
)}).first
->second
.get();
600 void WatchmanAutoloadMapFactory::garbageCollectUnusedAutoloadMaps(
601 std::chrono::seconds expirationTime
) {
602 auto mapsToRemove
= [&]() -> std::vector
<std::shared_ptr
<FactsStore
>> {
603 std::unique_lock g
{m_mutex
};
605 // If a map was last used before this time, remove it
606 auto deadline
= std::chrono::steady_clock::now() - expirationTime
;
608 std::vector
<WatchmanAutoloadMapKey
> keysToRemove
;
609 for (auto const& [mapKey
, _
] : m_maps
) {
610 auto lastUsedIt
= m_lastUsed
.find(mapKey
);
611 if (lastUsedIt
== m_lastUsed
.end() || lastUsedIt
->second
< deadline
) {
612 keysToRemove
.push_back(mapKey
);
616 std::vector
<std::shared_ptr
<FactsStore
>> maps
;
617 maps
.reserve(keysToRemove
.size());
618 for (auto const& mapKey
: keysToRemove
) {
619 FTRACE(2, "Evicting WatchmanAutoloadMap: {}\n", mapKey
.toString());
620 auto it
= m_maps
.find(mapKey
);
621 if (it
!= m_maps
.end()) {
622 maps
.push_back(std::move(it
->second
));
625 m_lastUsed
.erase(mapKey
);
630 // Final references to shared_ptr<Facts> fall out of scope
631 // while `m_mutex` lock is not held
634 FactsStore
& getFactsOrThrow() {
635 auto* facts
= AutoloadHandler::s_instance
->getFacts();
636 if (facts
== nullptr) {
637 SystemLib::throwInvalidOperationExceptionObject(
638 "Native Facts is not enabled. Call HH\\Facts\\enabled() to determine "
639 "if native Facts is enabled for the current request.");
647 bool HHVM_FUNCTION(facts_enabled
) {
648 return AutoloadHandler::s_instance
->getFacts() != nullptr;
651 Variant
HHVM_FUNCTION(facts_db_path
, const String
& rootStr
) {
652 // Turn rootStr into an absolute path.
653 auto root
= [&]() -> std::optional
<folly::fs::path
> {
654 folly::fs::path maybeRoot
{rootStr
.get()->slice()};
655 if (maybeRoot
.is_absolute()) {
658 // The given root is a relative path, so find the directory where the
659 // current request's `.hhvmconfig.hdf` file lives and resolve relative to
661 auto requestOptions
= g_context
->getRepoOptionsForRequest();
662 if (!requestOptions
) {
665 return folly::fs::path
{requestOptions
->path()}.parent_path() / maybeRoot
;
668 FTRACE(2, "Error resolving {}\n", rootStr
.slice());
669 return Variant
{Variant::NullInit
{}};
671 assertx(root
->is_absolute());
673 auto optionPath
= *root
/ ".hhvmconfig.hdf";
674 FTRACE(3, "Got options at {}\n", optionPath
.native());
675 auto const& repoOptions
= RepoOptions::forFile(optionPath
.native().c_str());
678 return Variant
{Facts::WatchmanAutoloadMapKey::get(repoOptions
)
679 .m_dbData
.m_path
.native()};
680 } catch (const Facts::RepoOptionsParseExc
& e
) {
681 throw_invalid_operation_exception(makeStaticString(e
.what()));
685 Variant
HHVM_FUNCTION(facts_type_to_path
, const String
& typeName
) {
686 auto path
= Facts::getFactsOrThrow().getTypeFile(typeName
);
688 return Variant
{Variant::NullInit
{}};
690 return Variant
{*path
};
694 Variant
HHVM_FUNCTION(facts_function_to_path
, const String
& functionName
) {
695 auto path
= Facts::getFactsOrThrow().getFunctionFile(functionName
);
697 return Variant
{Variant::NullInit
{}};
699 return Variant
{*path
};
703 Variant
HHVM_FUNCTION(facts_constant_to_path
, const String
& constantName
) {
704 auto path
= Facts::getFactsOrThrow().getConstantFile(constantName
);
706 return Variant
{Variant::NullInit
{}};
708 return Variant
{*path
};
712 Variant
HHVM_FUNCTION(facts_type_alias_to_path
, const String
& typeAliasName
) {
713 auto path
= Facts::getFactsOrThrow().getTypeAliasFile(typeAliasName
);
715 return Variant
{Variant::NullInit
{}};
717 return Variant
{*path
};
721 Array
HHVM_FUNCTION(facts_path_to_types
, const String
& path
) {
722 return Facts::getFactsOrThrow().getFileTypes(path
);
725 Array
HHVM_FUNCTION(facts_path_to_functions
, const String
& path
) {
726 return Facts::getFactsOrThrow().getFileFunctions(path
);
729 Array
HHVM_FUNCTION(facts_path_to_constants
, const String
& path
) {
730 return Facts::getFactsOrThrow().getFileConstants(path
);
733 Array
HHVM_FUNCTION(facts_path_to_type_aliases
, const String
& path
) {
734 return Facts::getFactsOrThrow().getFileTypeAliases(path
);
737 Variant
HHVM_FUNCTION(facts_type_name
, const String
& type
) {
738 return Facts::getFactsOrThrow().getTypeName(type
);
741 Variant
HHVM_FUNCTION(facts_kind
, const String
& type
) {
742 return Facts::getFactsOrThrow().getKind(type
);
745 bool HHVM_FUNCTION(facts_is_abstract
, const String
& type
) {
746 return Facts::getFactsOrThrow().isTypeAbstract(type
);
749 bool HHVM_FUNCTION(facts_is_final
, const String
& type
) {
750 return Facts::getFactsOrThrow().isTypeFinal(type
);
754 facts_subtypes
, const String
& baseType
, const Variant
& filters
) {
755 return Facts::getFactsOrThrow().getDerivedTypes(baseType
, filters
);
759 facts_transitive_subtypes
, const String
& baseType
, const Variant
& filters
) {
760 return Facts::getFactsOrThrow().getTransitiveDerivedTypes(baseType
, filters
);
764 facts_supertypes
, const String
& derivedType
, const Variant
& filters
) {
765 return Facts::getFactsOrThrow().getBaseTypes(derivedType
, filters
);
768 Array
HHVM_FUNCTION(facts_types_with_attribute
, const String
& attr
) {
769 return Facts::getFactsOrThrow().getTypesWithAttribute(attr
);
772 Array
HHVM_FUNCTION(facts_type_aliases_with_attribute
, const String
& attr
) {
773 return Facts::getFactsOrThrow().getTypeAliasesWithAttribute(attr
);
776 Array
HHVM_FUNCTION(facts_type_attributes
, const String
& type
) {
777 return Facts::getFactsOrThrow().getTypeAttributes(type
);
780 Array
HHVM_FUNCTION(facts_type_alias_attributes
, const String
& typeAlias
) {
781 return Facts::getFactsOrThrow().getTypeAttributes(typeAlias
);
785 facts_type_attribute_parameters
, const String
& type
, const String
& attr
) {
786 return Facts::getFactsOrThrow().getTypeAttrArgs(type
, attr
);
790 facts_type_alias_attribute_parameters
,
792 const String
& attr
) {
793 return Facts::getFactsOrThrow().getTypeAttrArgs(type
, attr
);
796 Array
HHVM_FUNCTION(facts_all_types
) {
797 return Facts::getFactsOrThrow().getAllTypes();
799 Array
HHVM_FUNCTION(facts_all_functions
) {
800 return Facts::getFactsOrThrow().getAllFunctions();
802 Array
HHVM_FUNCTION(facts_all_constants
) {
803 return Facts::getFactsOrThrow().getAllConstants();
805 Array
HHVM_FUNCTION(facts_all_type_aliases
) {
806 return Facts::getFactsOrThrow().getAllTypeAliases();