2 +----------------------------------------------------------------------+
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"
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"
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() {
50 void FactsFactory::setInstance(FactsFactory
* instance
) {
51 s_mapFactory
= instance
;
54 //////////////////////////////////////////////////////////////////////
58 FactsStore
* getFactsForRequest() {
59 auto* factory
= FactsFactory::getInstance();
64 if (g_context
.isNull()) {
68 auto* repoOptions
= g_context
->getRepoOptionsForRequest();
74 auto* map
= factory
->getForOptions(*repoOptions
);
80 tracing::Block _
{"autoload-ensure-updated"};
83 } catch (const std::exception
& e
) {
84 auto repoRoot
= folly::fs::canonical(repoOptions
->path()).parent_path();
86 "Failed to update native autoloader, not natively autoloading %s. %s\n",
87 repoRoot
.generic().c_str(),
95 void AutoloadHandler::requestInit() {
99 m_facts
= getFactsForRequest();
100 if (RuntimeOption::RepoAuthoritative
) {
101 m_map
= s_repoAutoloadMap
.get();
108 void AutoloadHandler::requestShutdown() {
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();
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 {
131 m_ne
= NamedEntity::get(m_name
, 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 {
148 m_ne
= NamedEntity::get(m_name
.get(), false);
153 return m_ne
->getCachedClass() != nullptr;
156 struct ConstExistsChecker
{
157 const StringData
* m_name
;
158 explicit ConstExistsChecker(const StringData
* 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 {
171 m_ne
= NamedEntity::get(m_name
.get(), 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 {
187 m_ne
= NamedEntity::get(m_name
.get(), false);
192 return m_ne
->getCachedClass() != nullptr ||
193 m_ne
->getCachedTypeAlias() != nullptr;
203 Optional
<String
> AutoloadHandler::getFile(const String
& clsName
,
204 AutoloadMap::KindOf kind
) {
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
) {
213 return m_map
->getSymbols(kind
, path
);
218 AutoloadHandler::loadFromMapImpl(const String
& clsName
,
219 AutoloadMap::KindOf kind
,
220 const T
&checkExists
,
222 auto file
= getFile(clsName
, kind
);
224 return AutoloadMap::Result::Failure
;
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
);
237 auto const unit
= lookupUnit(fName
.get(), "", &initial
,
238 Native::s_noNativeFuncs
,
239 RuntimeOption::TrustAutoloaderPath
);
241 if (initial
) unit
->merge();
244 } catch (ExitException
& ee
) {
246 } catch (ResourceExceededException
& ree
) {
248 } catch (ExtendedException
& ee
) {
249 auto fileAndLine
= ee
.getFileAndLine();
251 (fileAndLine
.first
.empty())
253 : folly::format("{} in {} on line {}",
254 ee
.getMessage(), fileAndLine
.first
,
255 fileAndLine
.second
).str();
256 if (RuntimeOption::AutoloadRethrowExceptions
) {
259 log_err(msg
.c_str());
261 } catch (Exception
& e
) {
262 auto msg
= e
.getMessage();
263 if (RuntimeOption::AutoloadRethrowExceptions
) {
266 log_err(msg
.c_str());
268 } catch (Object
& e
) {
269 log_err(e
.toString().c_str());
272 String msg
= "Unknown exception";
273 log_err(msg
.c_str());
276 if (ok
&& checkExists()) {
277 return AutoloadMap::Result::Success
;
279 return AutoloadMap::Result::Failure
;
284 AutoloadHandler::loadFromMap(const String
& clsName
,
285 AutoloadMap::KindOf kind
,
286 const T
&checkExists
) {
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
) {
306 bool AutoloadHandler::autoloadFunc(StringData
* name
) {
307 tracing::BlockNoTrace _
{
308 (m_map
&& m_map
->isNative()) ? "autoload-native" : "autoload"
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"
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"
331 loadFromMap(name
, AutoloadMap::KindOf::TypeAlias
,
332 TypeExistsChecker(name
)) != AutoloadMap::Result::Failure
;
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
) {
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())) {
367 ClassExistsChecker
ce(className
);
368 AutoloadMap::Result res
= loadFromMap(className
, AutoloadMap::KindOf::Type
,
370 if (res
== AutoloadMap::Result::Success
|| ce()) return true;
376 AutoloadHandler::loadFromMapPartial(const String
& className
,
377 AutoloadMap::KindOf kind
,
378 const T
&checkExists
,
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
);
386 if (m_map
->canHandleFailure()) {
387 res
= m_map
->handleFailure(kind
, className
, err
);
388 assertx(res
!= AutoloadMap::Result::Failure
);
390 return AutoloadMap::Result::Success
;
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
);
408 NamedTypeExistsChecker
cte(className
);
409 bool tryType
= true, tryTypeAlias
= true;
410 AutoloadMap::Result typeRes
= AutoloadMap::Result::RetryAutoloading
,
411 typeAliasRes
= AutoloadMap::Result::RetryAutoloading
;
413 Variant typeErr
{Variant::NullInit()};
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
,
420 if (typeRes
== AutoloadMap::Result::Success
) return true;
422 Variant typeAliasErr
{Variant::NullInit()};
424 // Next, try consulting the 'type alias' map. Again, don't call the
425 // failure callback unless there was an uncaught exception
427 typeAliasRes
= loadFromMapPartial(className
,
428 AutoloadMap::KindOf::TypeAlias
, cte
,
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
439 if (typeRes
== AutoloadMap::Result::Failure
) {
441 typeRes
= m_map
->handleFailure(AutoloadMap::KindOf::Type
,
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
) {
472 void AutoloadHandler::setRepoAutoloadMap(std::unique_ptr
<RepoAutoloadMap
> map
) {
473 assertx(RO::RepoAuthoritative
);
474 assertx(!s_repoAutoloadMap
);
475 s_repoAutoloadMap
= std::move(map
);
478 //////////////////////////////////////////////////////////////////////