remove last uses of DECLARE_STATIC_REQUEST_LOCAL
[hiphop-php.git] / hphp / runtime / base / autoload-handler.cpp
blob063532730f35991c56c214205c0077e7eeee3071
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file 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 +----------------------------------------------------------------------+
16 #include "hphp/runtime/base/autoload-handler.h"
18 #include <algorithm>
20 #include <folly/experimental/io/FsUtil.h>
22 #include "hphp/runtime/base/array-init.h"
23 #include "hphp/runtime/base/builtin-functions.h"
24 #include "hphp/runtime/base/type-string.h"
25 #include "hphp/runtime/base/tv-refcount.h"
26 #include "hphp/runtime/base/container-functions.h"
27 #include "hphp/runtime/base/runtime-option.h"
28 #include "hphp/runtime/base/stat-cache.h"
29 #include "hphp/runtime/base/repo-autoload-map.h"
30 #include "hphp/runtime/base/unit-cache.h"
31 #include "hphp/runtime/vm/repo-global-data.h"
32 #include "hphp/runtime/vm/unit.h"
33 #include "hphp/runtime/vm/unit-util.h"
34 #include "hphp/runtime/vm/vm-regs.h"
36 namespace HPHP {
38 //////////////////////////////////////////////////////////////////////
40 RDS_LOCAL(AutoloadHandler, AutoloadHandler::s_instance);
42 static FactsFactory* s_mapFactory = nullptr;
44 std::unique_ptr<RepoAutoloadMap> AutoloadHandler::s_repoAutoloadMap{};
46 FactsFactory* FactsFactory::getInstance() {
47 return s_mapFactory;
50 void FactsFactory::setInstance(FactsFactory* instance) {
51 s_mapFactory = instance;
54 //////////////////////////////////////////////////////////////////////
56 namespace {
58 FactsStore* getFactsForRequest() {
59 auto* factory = FactsFactory::getInstance();
60 if (!factory) {
61 return nullptr;
64 if (g_context.isNull()) {
65 return nullptr;
68 auto* repoOptions = g_context->getRepoOptionsForRequest();
69 if (!repoOptions) {
70 return nullptr;
74 auto* map = factory->getForOptions(*repoOptions);
75 if (!map) {
76 return nullptr;
79 try {
80 tracing::Block _{"autoload-ensure-updated"};
81 map->ensureUpdated();
82 return map;
83 } catch (const std::exception& e) {
84 auto repoRoot = folly::fs::canonical(repoOptions->path()).parent_path();
85 Logger::Info(
86 "Failed to update native autoloader, not natively autoloading %s. %s\n",
87 repoRoot.generic().c_str(),
88 e.what());
90 return nullptr;
93 } // namespace
95 void AutoloadHandler::requestInit() {
96 assertx(!m_map);
97 assertx(!m_facts);
98 assertx(!m_req_map);
99 m_facts = getFactsForRequest();
100 if (RuntimeOption::RepoAuthoritative) {
101 m_map = s_repoAutoloadMap.get();
102 assertx(m_map);
103 } else {
104 m_map = m_facts;
108 void AutoloadHandler::requestShutdown() {
109 m_map = nullptr;
110 m_facts = nullptr;
111 m_req_map = nullptr;
114 bool AutoloadHandler::setMap(const Array& map, String root) {
115 assertx(!RuntimeOption::RepoAuthoritative);
117 m_req_map = req::make_unique<UserAutoloadMap>(
118 UserAutoloadMap::fromFullMap(map, std::move(root)));
119 m_map = m_req_map.get();
120 return true;
123 namespace {
124 struct FuncExistsChecker {
125 const StringData* m_name;
126 mutable NamedEntity* m_ne;
127 explicit FuncExistsChecker(const StringData* name)
128 : m_name(name), m_ne(nullptr) {}
129 bool operator()() const {
130 if (!m_ne) {
131 m_ne = NamedEntity::get(m_name, false);
132 if (!m_ne) {
133 return false;
136 auto f = m_ne->getCachedFunc();
137 return (f != nullptr) &&
138 (f->arFuncPtr() != Native::unimplementedWrapper);
141 struct ClassExistsChecker {
142 const String& m_name;
143 mutable NamedEntity* m_ne;
144 explicit ClassExistsChecker(const String& name)
145 : m_name(name), m_ne(nullptr) {}
146 bool operator()() const {
147 if (!m_ne) {
148 m_ne = NamedEntity::get(m_name.get(), false);
149 if (!m_ne) {
150 return false;
153 return m_ne->getCachedClass() != nullptr;
156 struct ConstExistsChecker {
157 const StringData* m_name;
158 explicit ConstExistsChecker(const StringData* name)
159 : m_name(name) {}
160 bool operator()() const {
161 return type(Constant::lookup(m_name)) != KindOfUninit;
164 struct TypeExistsChecker {
165 const String& m_name;
166 mutable NamedEntity* m_ne;
167 explicit TypeExistsChecker(const String& name)
168 : m_name(name), m_ne(nullptr) {}
169 bool operator()() const {
170 if (!m_ne) {
171 m_ne = NamedEntity::get(m_name.get(), false);
172 if (!m_ne) {
173 return false;
176 return m_ne->getCachedTypeAlias() != nullptr;
180 struct NamedTypeExistsChecker {
181 const String& m_name;
182 mutable NamedEntity* m_ne;
183 explicit NamedTypeExistsChecker(const String& name)
184 : m_name(name), m_ne(nullptr) {}
185 bool operator()() const {
186 if (!m_ne) {
187 m_ne = NamedEntity::get(m_name.get(), false);
188 if (!m_ne) {
189 return false;
192 return m_ne->getCachedClass() != nullptr ||
193 m_ne->getCachedTypeAlias() != nullptr;
199 const StaticString
200 s_file("file"),
201 s_line("line");
203 Optional<String> AutoloadHandler::getFile(const String& clsName,
204 AutoloadMap::KindOf kind) {
205 assertx(m_map);
206 // Always normalize name before autoloading
207 return m_map->getFile(kind, normalizeNS(clsName));
210 Array AutoloadHandler::getSymbols(const String& path,
211 AutoloadMap::KindOf kind) {
212 assertx(m_map);
213 return m_map->getSymbols(kind, path);
216 template <class T>
217 AutoloadMap::Result
218 AutoloadHandler::loadFromMapImpl(const String& clsName,
219 AutoloadMap::KindOf kind,
220 const T &checkExists,
221 Variant& err) {
222 auto file = getFile(clsName, kind);
223 if (!file) {
224 return AutoloadMap::Result::Failure;
226 bool ok = false;
227 String fName = *file;
228 // Utility for logging errors in server mode.
229 auto log_err = [](char const* const msg) {
230 if (RuntimeOption::ServerMode) {
231 Logger::Error("Exception: AutoloadMap::loadFromMapImpl: %s", msg);
234 try {
235 VMRegAnchor _;
236 bool initial;
237 auto const unit = lookupUnit(fName.get(), "", &initial,
238 Native::s_noNativeFuncs,
239 RuntimeOption::TrustAutoloaderPath);
240 if (unit) {
241 if (initial) unit->merge();
242 ok = true;
244 } catch (ExitException& ee) {
245 throw;
246 } catch (ResourceExceededException& ree) {
247 throw;
248 } catch (ExtendedException& ee) {
249 auto fileAndLine = ee.getFileAndLine();
250 std::string msg =
251 (fileAndLine.first.empty())
252 ? ee.getMessage()
253 : folly::format("{} in {} on line {}",
254 ee.getMessage(), fileAndLine.first,
255 fileAndLine.second).str();
256 if (RuntimeOption::AutoloadRethrowExceptions) {
257 throw;
259 log_err(msg.c_str());
260 err = msg;
261 } catch (Exception& e) {
262 auto msg = e.getMessage();
263 if (RuntimeOption::AutoloadRethrowExceptions) {
264 throw;
266 log_err(msg.c_str());
267 err = msg;
268 } catch (Object& e) {
269 log_err(e.toString().c_str());
270 err = e;
271 } catch (...) {
272 String msg = "Unknown exception";
273 log_err(msg.c_str());
274 err = msg;
276 if (ok && checkExists()) {
277 return AutoloadMap::Result::Success;
279 return AutoloadMap::Result::Failure;
282 template <class T>
283 AutoloadMap::Result
284 AutoloadHandler::loadFromMap(const String& clsName,
285 AutoloadMap::KindOf kind,
286 const T &checkExists) {
287 assertx(m_map);
288 while (true) {
289 Variant err{Variant::NullInit()};
290 AutoloadMap::Result res = loadFromMapImpl(clsName, kind, checkExists, err);
291 if (res == AutoloadMap::Result::Success) {
292 return AutoloadMap::Result::Success;
294 if (!m_map->canHandleFailure()) {
295 return AutoloadMap::Result::Failure;
297 res = m_map->handleFailure(kind, clsName, err);
298 if (checkExists()) return AutoloadMap::Result::Success;
299 if (res == AutoloadMap::Result::RetryAutoloading) {
300 continue;
302 return res;
306 bool AutoloadHandler::autoloadFunc(StringData* name) {
307 tracing::BlockNoTrace _{
308 (m_map && m_map->isNative()) ? "autoload-native" : "autoload"
310 return m_map &&
311 loadFromMap(String{name},
312 AutoloadMap::KindOf::Function,
313 FuncExistsChecker(name)) != AutoloadMap::Result::Failure;
316 bool AutoloadHandler::autoloadConstant(StringData* name) {
317 tracing::BlockNoTrace _{
318 (m_map && m_map->isNative()) ? "autoload-native" : "autoload"
320 return m_map &&
321 loadFromMap(String{name},
322 AutoloadMap::KindOf::Constant,
323 ConstExistsChecker(name)) != AutoloadMap::Result::Failure;
326 bool AutoloadHandler::autoloadType(const String& name) {
327 tracing::BlockNoTrace _{
328 (m_map && m_map->isNative()) ? "autoload-native" : "autoload"
330 return m_map &&
331 loadFromMap(name, AutoloadMap::KindOf::TypeAlias,
332 TypeExistsChecker(name)) != AutoloadMap::Result::Failure;
336 * Taken from php-src
337 * https://github.com/php/php-src/blob/PHP-5.6/Zend/zend_execute_API.c#L960
339 bool is_valid_class_name(folly::StringPiece className) {
340 return strspn(
341 className.data(),
342 "0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\177"
343 "\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220"
344 "\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241"
345 "\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262"
346 "\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303"
347 "\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324"
348 "\325\326\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345"
349 "\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366"
350 "\367\370\371\372\373\374\375\376\377\\"
351 ) == className.size();
354 bool AutoloadHandler::autoloadClass(const String& clsName) {
355 tracing::BlockNoTrace _{
356 (m_map && m_map->isNative()) ? "autoload-native" : "autoload"
358 if (clsName.empty()) return false;
359 const String& className = normalizeNS(clsName);
360 // Verify class name before trying to load it
361 if (!is_valid_class_name(className.slice())) {
362 return false;
364 if (!m_map) {
365 return false;
367 ClassExistsChecker ce(className);
368 AutoloadMap::Result res = loadFromMap(className, AutoloadMap::KindOf::Type,
369 ce);
370 if (res == AutoloadMap::Result::Success || ce()) return true;
371 return false;
374 template <class T>
375 AutoloadMap::Result
376 AutoloadHandler::loadFromMapPartial(const String& className,
377 AutoloadMap::KindOf kind,
378 const T &checkExists,
379 Variant& err) {
380 AutoloadMap::Result res = loadFromMapImpl(className, kind, checkExists, err);
381 if (res == AutoloadMap::Result::Success) {
382 return AutoloadMap::Result::Success;
384 assertx(res == AutoloadMap::Result::Failure);
385 if (!err.isNull()) {
386 if (m_map->canHandleFailure()) {
387 res = m_map->handleFailure(kind, className, err);
388 assertx(res != AutoloadMap::Result::Failure);
389 if (checkExists()) {
390 return AutoloadMap::Result::Success;
394 return res;
397 bool AutoloadHandler::autoloadNamedType(const String& clsName) {
398 tracing::BlockNoTrace _{
399 (m_map && m_map->isNative()) ? "autoload-native" : "autoload"
402 if (clsName.empty()) return false;
403 const String& className = normalizeNS(clsName);
404 if (!m_map) {
405 return false;
408 NamedTypeExistsChecker cte(className);
409 bool tryType = true, tryTypeAlias = true;
410 AutoloadMap::Result typeRes = AutoloadMap::Result::RetryAutoloading,
411 typeAliasRes = AutoloadMap::Result::RetryAutoloading;
412 while (true) {
413 Variant typeErr{Variant::NullInit()};
414 if (tryType) {
415 // Try consulting the 'type' map first, but don't call the failure
416 // callback unless there was an uncaught exception or a fatal error
417 // during the include operation.
418 typeRes = loadFromMapPartial(className, AutoloadMap::KindOf::Type, cte,
419 typeErr);
420 if (typeRes == AutoloadMap::Result::Success) return true;
422 Variant typeAliasErr{Variant::NullInit()};
423 if (tryTypeAlias) {
424 // Next, try consulting the 'type alias' map. Again, don't call the
425 // failure callback unless there was an uncaught exception
426 // or fatal error.
427 typeAliasRes = loadFromMapPartial(className,
428 AutoloadMap::KindOf::TypeAlias, cte,
429 typeAliasErr);
430 if (typeAliasRes == AutoloadMap::Result::Success) return true;
432 // If we reach this point, then for each map either nothing was
433 // found or the file we included didn't define a class or type
434 // alias with the specified name, and the failure callback (if one
435 // exists) did not throw or raise a fatal error.
436 if (m_map->canHandleFailure()) {
437 // First, call the failure callback for 'class' if we didn't do so
438 // above
439 if (typeRes == AutoloadMap::Result::Failure) {
440 assertx(tryType);
441 typeRes = m_map->handleFailure(AutoloadMap::KindOf::Type,
442 className, typeErr);
443 // The failure callback may have defined a class for us, in
444 // which case we're done.
445 if (cte()) return true;
447 // Next, call the failure callback for 'type alias'
448 // if we didn't do so above
449 if (typeAliasRes == AutoloadMap::Result::Failure) {
450 assertx(tryTypeAlias);
451 typeAliasRes = m_map->handleFailure(AutoloadMap::KindOf::TypeAlias,
452 className, typeAliasErr);
453 // The failure callback may have defined a class or type alias for
454 // us, in which case we're done.
455 if (cte()) return true;
457 assertx(typeRes != AutoloadMap::Result::Failure &&
458 typeAliasRes != AutoloadMap::Result::Failure);
459 tryType = (typeRes == AutoloadMap::Result::RetryAutoloading);
460 tryTypeAlias = (typeAliasRes == AutoloadMap::Result::RetryAutoloading);
461 // If the failure callback requested a retry for 'class' or
462 // 'type' jump back to the top to try again.
463 if (tryType || tryTypeAlias) {
464 continue;
468 return false;
472 void AutoloadHandler::setRepoAutoloadMap(std::unique_ptr<RepoAutoloadMap> map) {
473 assertx(RO::RepoAuthoritative);
474 assertx(!s_repoAutoloadMap);
475 s_repoAutoloadMap = std::move(map);
478 //////////////////////////////////////////////////////////////////////