2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 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 +----------------------------------------------------------------------+
17 #include "hphp/runtime/base/builtin-functions.h"
19 #include "hphp/runtime/base/type-conversions.h"
20 #include "hphp/runtime/base/code-coverage.h"
21 #include "hphp/runtime/base/externals.h"
22 #include "hphp/runtime/base/variable-serializer.h"
23 #include "hphp/runtime/base/variable-unserializer.h"
24 #include "hphp/runtime/base/runtime-option.h"
25 #include "hphp/runtime/base/execution-context.h"
26 #include "hphp/runtime/base/strings.h"
27 #include "hphp/runtime/base/file-repository.h"
28 #include "hphp/runtime/debugger/debugger.h"
29 #include "hphp/runtime/ext/ext_process.h"
30 #include "hphp/runtime/ext/ext_function.h"
31 #include "hphp/runtime/ext/ext_file.h"
32 #include "hphp/runtime/ext/ext_collections.h"
33 #include "hphp/runtime/ext/ext_string.h"
34 #include "hphp/util/logger.h"
35 #include "hphp/util/process.h"
36 #include "hphp/runtime/vm/repo.h"
37 #include "hphp/runtime/vm/jit/translator.h"
38 #include "hphp/runtime/vm/jit/translator-inline.h"
39 #include "hphp/runtime/vm/unit.h"
40 #include "hphp/runtime/vm/unit-util.h"
41 #include "hphp/runtime/vm/event-hook.h"
42 #include "hphp/system/systemlib.h"
43 #include "folly/Format.h"
44 #include "hphp/util/text-util.h"
45 #include "hphp/util/string-vsnprintf.h"
46 #include "hphp/runtime/base/file-util.h"
47 #include "hphp/runtime/base/container-functions.h"
48 #include "hphp/runtime/base/request-injection-data.h"
57 ///////////////////////////////////////////////////////////////////////////////
61 s_offsetExists("offsetExists"),
62 s___autoload("__autoload"),
64 s___callStatic("__callStatic"),
65 s___invoke("__invoke"),
66 s_exception("exception"),
67 s_previous("previous"),
72 s_function("function"),
73 s_constant("constant"),
77 ///////////////////////////////////////////////////////////////////////////////
79 typedef smart::unique_ptr
<CufIter
> SmartCufIterPtr
;
81 bool array_is_valid_callback(const Array
& arr
) {
82 if (arr
.size() != 2 || !arr
.exists(int64_t(0)) || !arr
.exists(int64_t(1))) {
85 Variant elem0
= arr
.rvalAt(int64_t(0));
86 if (!elem0
.isString() && !elem0
.isObject()) {
89 Variant elem1
= arr
.rvalAt(int64_t(1));
90 if (!elem1
.isString()) {
97 vm_decode_function(const Variant
& function
,
102 StringData
*& invName
,
103 bool warn
/* = true */) {
105 if (function
.isString() || function
.isArray()) {
106 HPHP::Class
* ctx
= nullptr;
107 if (ar
) ctx
= arGetContextClass(ar
);
108 // Decode the 'function' parameter into this_, cls, name, pos, and
109 // nameContainsClass.
113 int pos
= String::npos
;
114 bool nameContainsClass
= false;
115 if (function
.isString()) {
116 // If 'function' is a string we simply assign it to name and
117 // leave this_ and cls set to NULL.
118 name
= function
.toString();
119 pos
= name
.find("::");
121 (pos
!= 0 && pos
!= String::npos
&& pos
+ 2 < name
.size());
123 // If 'function' is an array with exactly two indices 0 and 1, we
124 // assign the value at index 1 to name and we use the value at index
125 // 0 to populate cls (if the value is a string) or this_ and cls (if
126 // the value is an object).
127 assert(function
.isArray());
128 Array arr
= function
.toArray();
129 if (!array_is_valid_callback(arr
)) {
131 throw_invalid_argument("function: not a valid callback array");
135 Variant elem1
= arr
.rvalAt(int64_t(1));
136 name
= elem1
.toString();
137 pos
= name
.find("::");
139 (pos
!= 0 && pos
!= String::npos
&& pos
+ 2 < name
.size());
140 Variant elem0
= arr
.rvalAt(int64_t(0));
141 if (elem0
.isString()) {
142 String sclass
= elem0
.toString();
143 if (sclass
.get()->isame(s_self
.get())) {
147 if (!nameContainsClass
) {
150 } else if (sclass
.get()->isame(s_parent
.get())) {
151 if (ctx
&& ctx
->parent()) {
154 if (!nameContainsClass
) {
157 } else if (sclass
.get()->isame(s_static
.get())) {
160 cls
= ar
->getThis()->getVMClass();
161 } else if (ar
->hasClass()) {
162 cls
= ar
->getClass();
166 if (warn
&& nameContainsClass
) {
167 String nameClass
= name
.substr(0, pos
);
168 if (nameClass
.get()->isame(s_self
.get()) ||
169 nameClass
.get()->isame(s_static
.get())) {
170 raise_warning("behavior of call_user_func(array('%s', '%s')) "
171 "is undefined", sclass
.data(), name
.data());
174 cls
= Unit::loadClass(sclass
.get());
178 throw_invalid_argument("function: class not found");
183 assert(elem0
.isObject());
184 this_
= elem0
.getObjectData();
185 cls
= this_
->getVMClass();
189 HPHP::Class
* cc
= cls
;
190 if (nameContainsClass
) {
191 String c
= name
.substr(0, pos
);
192 name
= name
.substr(pos
+ 2);
193 if (c
.get()->isame(s_self
.get())) {
202 } else if (c
.get()->isame(s_parent
.get())) {
205 } else if (ctx
&& ctx
->parent()) {
211 } else if (c
.get()->isame(s_static
.get())) {
214 cc
= ar
->getThis()->getVMClass();
215 } else if (ar
->hasClass()) {
220 cc
= Unit::loadClass(c
.get());
224 throw_invalid_argument("function: class not found");
229 if (!cls
->classof(cc
)) {
231 raise_warning("call_user_func expects parameter 1 to be a valid "
232 "callback, class '%s' is not a subclass of '%s'",
233 cls
->preClass()->name()->data(),
234 cc
->preClass()->name()->data());
239 // If there is not a current instance, cc trumps cls.
245 HPHP::Func
* f
= HPHP::Unit::loadFunc(name
.get());
248 throw_invalid_argument("function: method '%s' not found",
253 assert(f
&& f
->preClass() == nullptr);
257 CallType lookupType
= this_
? CallType::ObjMethod
: CallType::ClsMethod
;
258 const HPHP::Func
* f
=
259 g_context
->lookupMethodCtx(cc
, name
.get(), ctx
, lookupType
);
260 if (f
&& (f
->attrs() & AttrStatic
)) {
261 // If we found a method and its static, null out this_
265 // If we did not find a static method AND this_ is null AND there is a
266 // frame ar, check if the current instance from ar is compatible
267 ObjectData
* obj
= ar
->hasThis() ? ar
->getThis() : nullptr;
268 if (obj
&& obj
->instanceof(cls
)) {
270 cls
= obj
->getVMClass();
275 // If this_ is non-null AND we could not find a method, try
276 // looking up __call in cls's method table
277 f
= cls
->lookupMethod(s___call
.get());
278 assert(!f
|| !(f
->attrs() & AttrStatic
));
280 if (!f
&& lookupType
== CallType::ClsMethod
) {
281 f
= cls
->lookupMethod(s___callStatic
.get());
282 assert(!f
|| (f
->attrs() & AttrStatic
));
285 if (f
&& (cc
== cls
|| cc
->lookupMethod(f
->name()))) {
286 // We found __call or __callStatic!
287 // Stash the original name into invName.
288 invName
= name
.get();
289 invName
->incRefCount();
291 // Bail out if we couldn't find the method or __call
293 throw_invalid_argument("function: method '%s' not found",
301 if (f
->isClosureBody() && !this_
) {
303 raise_warning("cannot invoke closure body without closure object");
308 assert(f
&& f
->preClass());
309 // If this_ is non-NULL, then this_ is the current instance and cls is
310 // the class of the current instance.
311 assert(!this_
|| this_
->getVMClass() == cls
);
312 // If we are doing a forwarding call and this_ is null, set cls
313 // appropriately to propagate the current late bound class.
314 if (!this_
&& forwarding
&& ar
) {
315 HPHP::Class
* fwdCls
= nullptr;
316 ObjectData
* obj
= ar
->hasThis() ? ar
->getThis() : nullptr;
318 fwdCls
= obj
->getVMClass();
319 } else if (ar
->hasClass()) {
320 fwdCls
= ar
->getClass();
322 // Only forward the current late bound class if it is the same or
323 // a descendent of cls
324 if (fwdCls
&& fwdCls
->classof(cls
)) {
330 if (function
.isObject()) {
331 this_
= function
.asCObjRef().get();
333 const HPHP::Func
*f
= this_
->getVMClass()->lookupMethod(s___invoke
.get());
335 ((f
->attrs() & AttrStatic
) && !f
->isClosureBody())) {
336 // If __invoke is static, invoke it as such
337 cls
= this_
->getVMClass();
340 if (warn
&& f
== nullptr) {
341 raise_warning("call_user_func() expects parameter 1 to be a valid "
342 "callback, object of class %s given (no __invoke "
343 "method found)", this_
->getVMClass()->name()->data());
348 throw_invalid_argument("function: not string, closure, or array");
353 Variant
vm_call_user_func(const Variant
& function
, const Variant
& params
,
354 bool forwarding
/* = false */) {
355 ObjectData
* obj
= nullptr;
356 Class
* cls
= nullptr;
358 StringData
* invName
= nullptr;
359 const Func
* f
= vm_decode_function(function
, cf(), forwarding
,
361 if (f
== nullptr || (!isContainer(params
) && !params
.isNull())) {
362 return uninit_null();
365 g_context
->invokeFunc((TypedValue
*)&ret
, f
, params
, obj
, cls
,
366 nullptr, invName
, ExecutionContext::InvokeCuf
);
371 * Helper method from converting between a PHP function and a CufIter.
373 static bool vm_decode_function_cufiter(const Variant
& function
,
374 SmartCufIterPtr
& cufIter
) {
375 ObjectData
* obj
= nullptr;
376 Class
* cls
= nullptr;
378 StringData
* invName
= nullptr;
379 // Don't warn here, let the caller decide what to do if the func is nullptr.
380 const HPHP::Func
* func
= vm_decode_function(function
, cf(), false,
381 obj
, cls
, invName
, false);
382 if (func
== nullptr) {
386 cufIter
= smart::make_unique
<CufIter
>();
387 cufIter
->setFunc(func
);
388 cufIter
->setName(invName
);
390 cufIter
->setCtx(obj
);
393 cufIter
->setCtx(cls
);
400 * Wraps calling an (autoload) PHP function from a CufIter.
402 static Variant
vm_call_user_func_cufiter(const CufIter
& cufIter
,
403 const Array
& params
) {
404 ObjectData
* obj
= nullptr;
405 HPHP::Class
* cls
= nullptr;
406 StringData
* invName
= cufIter
.name();
407 const HPHP::Func
* f
= cufIter
.func();
409 if (uintptr_t(cufIter
.ctx()) & 1) {
410 cls
= (Class
*)(uintptr_t(cufIter
.ctx()) & ~1);
412 obj
= (ObjectData
*)cufIter
.ctx();
415 assert(!obj
|| !cls
);
417 invName
->incRefCount();
420 g_context
->invokeFunc((TypedValue
*)&ret
, f
, params
, obj
, cls
,
421 nullptr, invName
, ExecutionContext::InvokeCuf
);
425 Variant
invoke(const String
& function
, const Variant
& params
,
426 strhash_t hash
/* = -1 */, bool tryInterp
/* = true */,
427 bool fatal
/* = true */) {
428 Func
* func
= Unit::loadFunc(function
.get());
429 if (func
&& (isContainer(params
) || params
.isNull())) {
431 g_context
->invokeFunc(ret
.asTypedValue(), func
, params
);
434 return invoke_failed(function
.c_str(), fatal
);
437 Variant
invoke(const char *function
, const Variant
& params
, strhash_t hash
/* = -1 */,
438 bool tryInterp
/* = true */, bool fatal
/* = true */) {
439 String
funcName(function
, CopyString
);
440 return invoke(funcName
, params
, hash
, tryInterp
, fatal
);
443 Variant
invoke_static_method(const String
& s
, const String
& method
,
444 const Variant
& params
, bool fatal
/* = true */) {
445 HPHP::Class
* class_
= Unit::lookupClass(s
.get());
446 if (class_
== nullptr) {
447 o_invoke_failed(s
.data(), method
.data(), fatal
);
448 return uninit_null();
450 const HPHP::Func
* f
= class_
->lookupMethod(method
.get());
451 if (f
== nullptr || !(f
->attrs() & AttrStatic
) ||
452 (!isContainer(params
) && !params
.isNull())) {
453 o_invoke_failed(s
.data(), method
.data(), fatal
);
454 return uninit_null();
457 g_context
->invokeFunc((TypedValue
*)&ret
, f
, params
, nullptr, class_
);
461 Variant
invoke_failed(const Variant
& func
,
462 bool fatal
/* = true */) {
463 if (func
.isObject()) {
464 return o_invoke_failed(func
.toCObjRef()->o_getClassName().c_str(),
467 return invoke_failed(func
.toString().c_str(), fatal
);
471 Variant
invoke_failed(const char *func
,
472 bool fatal
/* = true */) {
474 throw InvalidFunctionCallException(func
);
476 raise_warning("call_user_func to non-existent function %s", func
);
481 Variant
o_invoke_failed(const char *cls
, const char *meth
,
482 bool fatal
/* = true */) {
484 string msg
= "Unknown method ";
488 throw FatalErrorException(msg
.c_str());
490 raise_warning("call_user_func to non-existent method %s::%s", cls
, meth
);
495 void NEVER_INLINE
raise_null_object_prop() {
496 raise_notice("Trying to get property of non-object");
499 void NEVER_INLINE
throw_null_get_object_prop() {
500 raise_error("Trying to get property of non-object");
503 void NEVER_INLINE
throw_null_object_prop() {
504 raise_error("Trying to set property of non-object");
507 void NEVER_INLINE
throw_invalid_property_name(const String
& name
) {
509 throw EmptyObjectPropertyException();
511 throw NullStartObjectPropertyException();
515 void throw_instance_method_fatal(const char *name
) {
516 if (!strstr(name
, "::__destruct")) {
517 raise_error("Non-static method %s() cannot be called statically", name
);
521 void throw_iterator_not_valid() {
522 Object
e(SystemLib::AllocInvalidOperationExceptionObject(
523 "Iterator is not valid"));
527 void throw_collection_modified() {
528 Object
e(SystemLib::AllocInvalidOperationExceptionObject(
529 "Collection was modified during iteration"));
533 void throw_collection_property_exception() {
534 Object
e(SystemLib::AllocInvalidOperationExceptionObject(
535 "Cannot access a property on a collection"));
539 void throw_collection_compare_exception() {
540 static const string
msg(
541 "Cannot use relational comparison operators (<, <=, >, >=) to compare "
542 "a collection with an integer, double, string, array, or object");
543 Object
e(SystemLib::AllocInvalidOperationExceptionObject(msg
));
547 void throw_param_is_not_container() {
548 static const string
msg("Parameter must be an array or collection");
549 Object
e(SystemLib::AllocInvalidArgumentExceptionObject(msg
));
553 void throw_cannot_modify_immutable_object(const char* className
) {
554 auto msg
= folly::format(
555 "Cannot modify immutable object of type {}",
558 Object
e(SystemLib::AllocInvalidOperationExceptionObject(msg
));
562 void check_collection_compare(ObjectData
* obj
) {
563 if (obj
&& obj
->isCollection()) throw_collection_compare_exception();
566 void check_collection_compare(ObjectData
* obj1
, ObjectData
* obj2
) {
567 if (obj1
&& obj2
&& (obj1
->isCollection() || obj2
->isCollection())) {
568 throw_collection_compare_exception();
572 void check_collection_cast_to_array() {
573 if (RuntimeOption::WarnOnCollectionToArray
) {
574 raise_warning("Casting a collection to an array is an expensive operation "
575 "and should be avoided where possible. To convert a "
576 "collection to an array without raising a warning, use the "
577 "toArray() method.");
581 Object
create_object_only(const String
& s
) {
582 return g_context
->createObjectOnly(s
.get());
585 Object
create_object(const String
& s
, const Array
& params
, bool init
/* = true */) {
586 return g_context
->createObject(s
.get(), params
, init
);
590 * This function is used when another thread is segfaulting---we just
591 * want to wait forever to give it a chance to write a stacktrace file
592 * (and maybe a core file).
594 void pause_forever() {
598 void throw_missing_min_arguments_nr(const char *fn
, int expected
, int got
,
600 TypedValue
*rv
/* = nullptr */) {
602 rv
->m_data
.num
= 0LL;
603 rv
->m_type
= KindOfNull
;
605 if (level
== 2 || RuntimeOption::ThrowMissingArguments
) {
607 raise_error(Strings::MISSING_MIN_ARGUMENT
, fn
, got
);
609 raise_error(Strings::MISSING_MIN_ARGUMENTS
, fn
, expected
, got
);
613 raise_warning(Strings::MISSING_MIN_ARGUMENT
, fn
, got
);
615 raise_warning(Strings::MISSING_MIN_ARGUMENTS
, fn
, expected
, got
);
620 void throw_missing_arguments_nr(const char *fn
, int expected
, int got
,
622 TypedValue
*rv
/* = nullptr */) {
624 rv
->m_data
.num
= 0LL;
625 rv
->m_type
= KindOfNull
;
627 if (level
== 2 || RuntimeOption::ThrowMissingArguments
) {
629 raise_error(Strings::MISSING_ARGUMENT
, fn
, "exactly", got
);
631 raise_error(Strings::MISSING_ARGUMENTS
, fn
, "exactly", expected
, got
);
635 raise_warning(Strings::MISSING_ARGUMENT
, fn
, "exactly", got
);
637 raise_warning(Strings::MISSING_ARGUMENTS
, fn
, "exactly", expected
, got
);
642 void throw_toomany_arguments_nr(const char *fn
, int num
, int level
/* = 0 */,
643 TypedValue
*rv
/* = nullptr */) {
645 rv
->m_data
.num
= 0LL;
646 rv
->m_type
= KindOfNull
;
648 if (level
== 2 || RuntimeOption::ThrowTooManyArguments
) {
649 raise_error("Too many arguments for %s(), expected %d", fn
, num
);
650 } else if (level
== 1 || RuntimeOption::WarnTooManyArguments
) {
651 raise_warning("Too many arguments for %s(), expected %d", fn
, num
);
655 void throw_wrong_arguments_nr(const char *fn
, int count
, int cmin
, int cmax
,
657 TypedValue
*rv
/* = nullptr */) {
659 rv
->m_data
.num
= 0LL;
660 rv
->m_type
= KindOfNull
;
662 if (cmin
>= 0 && count
< cmin
&& cmin
!= cmax
) {
663 throw_missing_min_arguments_nr(fn
, cmin
, count
, level
);
666 if (cmin
>= 0 && count
< cmin
) {
667 throw_missing_arguments_nr(fn
, cmin
, count
, level
);
670 if (cmax
>= 0 && count
> cmax
) {
671 throw_toomany_arguments_nr(fn
, cmax
, level
);
677 void throw_bad_type_exception(const char *fmt
, ...) {
681 string_vsnprintf(msg
, fmt
, ap
);
684 if (RuntimeOption::ThrowBadTypeExceptions
) {
685 throw InvalidOperandException(msg
.c_str());
688 raise_warning("Invalid operand type was used: %s", msg
.c_str());
691 void throw_expected_array_exception() {
692 const char* fn
= "(unknown)";
693 ActRec
*ar
= g_context
->getStackFrame();
695 fn
= ar
->m_func
->name()->data();
697 throw_bad_type_exception("%s expects array(s)", fn
);
700 void throw_expected_array_or_collection_exception() {
701 const char* fn
= "(unknown)";
702 ActRec
*ar
= g_context
->getStackFrame();
704 fn
= ar
->m_func
->name()->data();
706 throw_bad_type_exception("%s expects array(s) or collection(s)", fn
);
709 void throw_invalid_argument(const char *fmt
, ...) {
713 string_vsnprintf(msg
, fmt
, ap
);
716 if (RuntimeOption::ThrowInvalidArguments
) {
717 throw InvalidArgumentException(msg
.c_str());
720 raise_warning("Invalid argument: %s", msg
.c_str());
723 Variant
throw_fatal_unset_static_property(const char *s
, const char *prop
) {
724 raise_error("Attempt to unset static property %s::$%s", s
, prop
);
725 return uninit_null();
728 Exception
* generate_request_timeout_exception() {
729 Exception
* ret
= nullptr;
730 ThreadInfo
*info
= ThreadInfo::s_threadInfo
.getNoCheck();
731 RequestInjectionData
&data
= info
->m_reqInjectionData
;
733 bool cli
= RuntimeOption::ClientExecutionMode();
734 std::string exceptionMsg
= cli
?
735 "Maximum execution time of " :
736 "entire web request took longer than ";
737 exceptionMsg
+= folly::to
<std::string
>(data
.getTimeout());
738 exceptionMsg
+= cli
? " seconds exceeded" : " seconds and timed out";
739 Array exceptionStack
;
740 if (RuntimeOption::InjectedStackTrace
) {
741 exceptionStack
= g_context
->debugBacktrace(false, true, true);
743 ret
= new RequestTimeoutException(exceptionMsg
, exceptionStack
);
747 Exception
* generate_memory_exceeded_exception() {
748 Array exceptionStack
;
749 if (RuntimeOption::InjectedStackTrace
) {
750 exceptionStack
= g_context
->debugBacktrace(false, true, true);
752 return new RequestMemoryExceededException(
753 "request has exceeded memory limit", exceptionStack
);
756 Variant
unserialize_ex(const char* str
, int len
,
757 VariableUnserializer::Type type
,
758 const Array
& class_whitelist
/* = null_array */) {
759 if (str
== nullptr || len
<= 0) {
763 VariableUnserializer
vu(str
, len
, type
, true, class_whitelist
);
766 v
= vu
.unserialize();
767 } catch (FatalErrorException
&e
) {
769 } catch (Exception
&e
) {
770 raise_notice("Unable to unserialize: [%s]. %s.", str
,
771 e
.getMessage().c_str());
777 Variant
unserialize_ex(const String
& str
,
778 VariableUnserializer::Type type
,
779 const Array
& class_whitelist
/* = null_array */) {
780 return unserialize_ex(str
.data(), str
.size(), type
, class_whitelist
);
783 String
concat3(const String
& s1
, const String
& s2
, const String
& s3
) {
784 StringSlice r1
= s1
.slice();
785 StringSlice r2
= s2
.slice();
786 StringSlice r3
= s3
.slice();
787 int len
= r1
.len
+ r2
.len
+ r3
.len
;
788 StringData
* str
= StringData::Make(len
);
789 auto const r
= str
->bufferSlice();
790 memcpy(r
.ptr
, r1
.ptr
, r1
.len
);
791 memcpy(r
.ptr
+ r1
.len
, r2
.ptr
, r2
.len
);
792 memcpy(r
.ptr
+ r1
.len
+ r2
.len
, r3
.ptr
, r3
.len
);
797 String
concat4(const String
& s1
, const String
& s2
, const String
& s3
,
799 StringSlice r1
= s1
.slice();
800 StringSlice r2
= s2
.slice();
801 StringSlice r3
= s3
.slice();
802 StringSlice r4
= s4
.slice();
803 int len
= r1
.len
+ r2
.len
+ r3
.len
+ r4
.len
;
804 StringData
* str
= StringData::Make(len
);
805 auto const r
= str
->bufferSlice();
806 memcpy(r
.ptr
, r1
.ptr
, r1
.len
);
807 memcpy(r
.ptr
+ r1
.len
, r2
.ptr
, r2
.len
);
808 memcpy(r
.ptr
+ r1
.len
+ r2
.len
, r3
.ptr
, r3
.len
);
809 memcpy(r
.ptr
+ r1
.len
+ r2
.len
+ r3
.len
, r4
.ptr
, r4
.len
);
814 Variant
include_impl_invoke(const String
& file
, bool once
,
815 const char *currentDir
) {
816 if (file
[0] == '/') {
817 if (RuntimeOption::SandboxMode
|| !RuntimeOption::AlwaysUseRelativePath
) {
819 return invoke_file(file
, once
, currentDir
);
820 } catch(PhpFileDoesNotExistException
&e
) {}
823 String
rel_path(FileUtil::relativePath(RuntimeOption::SourceRoot
,
824 string(file
.data())));
826 // Don't try/catch - We want the exception to be passed along
827 return invoke_file(rel_path
, once
, currentDir
);
829 // Don't try/catch - We want the exception to be passed along
830 return invoke_file(file
, once
, currentDir
);
834 Variant
invoke_file(const String
& s
, bool once
, const char *currentDir
) {
836 if (invoke_file_impl(r
, s
, once
, currentDir
)) {
839 return throw_missing_file(s
.c_str());
842 bool invoke_file_impl(Variant
&res
, const String
& path
, bool once
,
843 const char *currentDir
) {
845 auto const u
= g_context
->lookupPhpFile(path
.get(),
846 currentDir
, &initial
);
847 if (u
== nullptr) return false;
848 if (!once
|| initial
) {
849 g_context
->invokeUnit((TypedValue
*)(&res
), u
);
855 * Used by include_impl. resolve_include() needs some way of checking the
856 * existence of a file path, which for hphpc means attempting to invoke it.
857 * This struct carries some context information needed for the invocation, as
858 * well as a place for the return value of invoking the file.
860 struct IncludeImplInvokeContext
{
862 const char* currentDir
;
867 static bool include_impl_invoke_context(const String
& file
, void* ctx
) {
868 struct IncludeImplInvokeContext
* context
= (IncludeImplInvokeContext
*)ctx
;
869 bool invoked_file
= false;
871 context
->returnValue
= include_impl_invoke(file
, context
->once
,
872 context
->currentDir
);
874 } catch (PhpFileDoesNotExistException
& e
) {
875 context
->returnValue
= false;
881 * tryFile is a pointer to a function that resolve_include() will use to
882 * determine if a path references a real file. ctx is a pointer to some context
883 * information that will be passed through to tryFile. (It's a hacky closure)
885 String
resolve_include(const String
& file
, const char* currentDir
,
886 bool (*tryFile
)(const String
& file
, void*), void* ctx
) {
887 const char* c_file
= file
.data();
889 if (!File::IsPlainFilePath(file
)) {
890 // URIs don't have an include path
891 if (tryFile(file
, ctx
)) {
895 } else if (c_file
[0] == '/') {
896 String can_path
= FileUtil::canonicalize(file
);
898 if (tryFile(can_path
, ctx
)) {
902 } else if ((c_file
[0] == '.' && (c_file
[1] == '/' || (
903 c_file
[1] == '.' && c_file
[2] == '/')))) {
905 String
path(String(g_context
->getCwd() + "/" + file
));
906 String can_path
= FileUtil::canonicalize(path
);
908 if (tryFile(can_path
, ctx
)) {
913 auto includePaths
= ThreadInfo::s_threadInfo
.getNoCheck()->
914 m_reqInjectionData
.getIncludePaths();
915 unsigned int path_count
= includePaths
.size();
917 for (int i
= 0; i
< (int)path_count
; i
++) {
919 String
includePath(includePaths
[i
]);
921 if (includePath
[0] != '/') {
922 path
+= (g_context
->getCwd() + "/");
927 if (path
[path
.size() - 1] != '/') {
932 String can_path
= FileUtil::canonicalize(path
);
934 if (tryFile(can_path
, ctx
)) {
939 if (currentDir
[0] == '/') {
940 String
path(currentDir
);
943 String can_path
= FileUtil::canonicalize(path
);
945 if (tryFile(can_path
, ctx
)) {
949 String
path(g_context
->getCwd() + "/" + currentDir
+ file
);
950 String can_path
= FileUtil::canonicalize(path
);
952 if (tryFile(can_path
, ctx
)) {
961 static Variant
include_impl(const String
& file
, bool once
,
962 const char *currentDir
, bool required
,
964 struct IncludeImplInvokeContext ctx
= {once
, currentDir
};
965 String can_path
= resolve_include(file
, currentDir
,
966 include_impl_invoke_context
, (void*)&ctx
);
968 if (can_path
.isNull()) {
971 raise_notice("Tried to invoke %s but file not found.", file
.data());
974 String ms
= "Required file that does not exist: ";
976 throw FatalErrorException(ms
.data());
981 return ctx
.returnValue
;
984 Variant
include(const String
& file
, bool once
/* = false */,
985 const char *currentDir
/* = NULL */,
986 bool raiseNotice
/*= true*/) {
987 return include_impl(file
, once
, currentDir
, false, raiseNotice
);
990 Variant
require(const String
& file
, bool once
/* = false */,
991 const char *currentDir
/* = NULL */,
992 bool raiseNotice
/*= true*/) {
993 return include_impl(file
, once
, currentDir
, true, raiseNotice
);
996 ///////////////////////////////////////////////////////////////////////////////
997 // class AutoloadHandler
999 IMPLEMENT_REQUEST_LOCAL(AutoloadHandler
, AutoloadHandler::s_instance
);
1001 void AutoloadHandler::requestInit() {
1002 assert(m_map
.get() == nullptr);
1003 assert(m_map_root
.get() == nullptr);
1004 assert(m_loading
.get() == nullptr);
1005 m_spl_stack_inited
= false;
1006 new (&m_handlers
) smart::deque
<HandlerBundle
>();
1009 void AutoloadHandler::requestShutdown() {
1013 // m_spl_stack_inited will be re-initialized by the next requestInit
1014 // m_handlers will be re-initialized by the next requestInit
1017 bool AutoloadHandler::setMap(const Array
& map
, const String
& root
) {
1019 this->m_map_root
= root
;
1023 class ClassExistsChecker
{
1025 ClassExistsChecker() {}
1026 bool operator()(const String
& name
) const {
1027 return Unit::lookupClass(name
.get()) != nullptr;
1031 class ConstantExistsChecker
{
1033 bool operator()(const String
& name
) const {
1034 return Unit::lookupCns(name
.get()) != nullptr;
1039 AutoloadHandler::Result
AutoloadHandler::loadFromMap(const String
& clsName
,
1042 const T
&checkExists
) {
1043 assert(!m_map
.isNull());
1045 // always normalize name before autoloading
1046 const String
& name
= normalizeNS(clsName
);
1049 const Variant
& type_map
= m_map
.get()->get(kind
);
1050 auto const typeMapCell
= type_map
.asCell();
1051 if (typeMapCell
->m_type
!= KindOfArray
) return Failure
;
1052 String canonicalName
= toLower
? f_strtolower(name
) : name
;
1053 const Variant
& file
= typeMapCell
->m_data
.parr
->get(canonicalName
);
1055 Variant err
{Variant::NullInit()};
1056 if (file
.isString()) {
1057 String fName
= file
.toCStrRef().get();
1058 if (fName
.get()->data()[0] != '/') {
1059 if (!m_map_root
.empty()) {
1060 fName
= m_map_root
+ fName
;
1066 auto const ec
= g_context
.getNoCheck();
1067 Unit
* u
= ec
->evalInclude(fName
.get(), nullptr, &initial
);
1071 ec
->invokeFunc(&retval
, u
->getMain(), init_null_variant
,
1072 nullptr, nullptr, nullptr, nullptr,
1073 ExecutionContext::InvokePseudoMain
);
1074 tvRefcountedDecRef(&retval
);
1078 } catch (Exception
& e
) {
1079 err
= e
.getMessage();
1080 } catch (Object
& e
) {
1083 err
= String("Unknown Exception");
1086 if (ok
&& checkExists(name
)) {
1089 const Variant
& func
= m_map
.get()->get(s_failure
);
1090 if (func
.isNull()) return Failure
;
1091 // can throw, otherwise
1092 // - true means the map was updated. try again
1093 // - false means we should stop applying autoloaders (only affects classes)
1094 // - anything else means keep going
1095 Variant action
= vm_call_user_func(func
,
1096 make_packed_array(kind
, name
, err
));
1097 auto const actionCell
= action
.asCell();
1098 if (actionCell
->m_type
== KindOfBoolean
) {
1099 if (actionCell
->m_data
.num
) continue;
1100 return StopAutoloading
;
1102 return ContinueAutoloading
;
1106 bool AutoloadHandler::autoloadFunc(StringData
* name
) {
1107 return !m_map
.isNull() &&
1108 loadFromMap(name
, s_function
, true, function_exists
) != Failure
;
1111 bool AutoloadHandler::autoloadConstant(StringData
* name
) {
1112 return !m_map
.isNull() &&
1113 loadFromMap(name
, s_constant
, false, ConstantExistsChecker()) != Failure
;
1116 bool AutoloadHandler::autoloadType(const String
& name
) {
1117 return !m_map
.isNull() &&
1118 loadFromMap(name
, s_type
, true,
1119 [] (const String
& name
) {
1120 return Unit::GetNamedEntity(name
.get())->
1121 getCachedTypeAlias() != nullptr;
1127 * invokeHandler returns true if any autoload handlers were executed,
1128 * false otherwise. When this function returns true, it is the caller's
1129 * responsibility to check if the given class or interface exists.
1131 bool AutoloadHandler::invokeHandler(const String
& clsName
,
1132 bool forceSplStack
/* = false */) {
1134 if (clsName
.empty()) return false;
1136 const String
& className
= normalizeNS(clsName
);
1138 if (!m_map
.isNull()) {
1139 ClassExistsChecker ce
;
1140 Result res
= loadFromMap(className
, s_class
, true, ce
);
1141 if (res
== ContinueAutoloading
) {
1142 if (ce(className
)) return true;
1144 if (res
!= Failure
) return res
== Success
;
1147 // If we end up in a recursive autoload loop where we try to load the
1148 // same class twice, just fail the load to mimic PHP as many frameworks
1149 // rely on it unless we are forcing a restart (due to spl_autoload_call)
1150 // in which case autoload is allowed to be reentrant.
1151 if (!forceSplStack
) {
1152 if (m_loading
.exists(className
)) { return false; }
1153 m_loading
.add(className
, className
);
1155 // We can still overflow the stack if there is a loop when using
1156 // spl_autoload_call directly, but this behavior matches the reference
1158 m_loading
.append(className
);
1161 // Make sure state is cleaned up from this load; autoloading of arbitrary
1162 // code below can throw
1164 String l_className
= m_loading
.pop();
1165 assert(l_className
== className
);
1168 Array params
= PackedArrayInit(1).append(className
).toArray();
1169 if (!m_spl_stack_inited
&& !forceSplStack
) {
1170 if (function_exists(s___autoload
)) {
1171 invoke(s___autoload
, params
, -1, true, false);
1176 if (!m_spl_stack_inited
|| m_handlers
.empty()) {
1179 Object autoloadException
;
1180 for (const HandlerBundle
& hb
: m_handlers
) {
1182 vm_call_user_func_cufiter(*hb
.m_cufIter
, params
);
1183 } catch (Object
& ex
) {
1184 assert(ex
.instanceof(SystemLib::s_ExceptionClass
));
1185 if (autoloadException
.isNull()) {
1186 autoloadException
= ex
;
1189 Variant next
= cur
->o_get(s_previous
, false, s_exception
);
1190 while (next
.isObject()) {
1191 cur
= next
.toObject();
1192 next
= cur
->o_get(s_previous
, false, s_exception
);
1194 cur
->o_set(s_previous
, autoloadException
, s_exception
);
1195 autoloadException
= ex
;
1198 if (Unit::lookupClass(className
.get()) != nullptr) {
1202 if (!autoloadException
.isNull()) {
1203 throw autoloadException
;
1208 Array
AutoloadHandler::getHandlers() {
1209 if (!m_spl_stack_inited
) {
1213 PackedArrayInit
handlers(m_handlers
.size());
1215 for (const HandlerBundle
& hb
: m_handlers
) {
1216 handlers
.append(hb
.m_handler
);
1219 return handlers
.toArray();
1222 bool AutoloadHandler::CompareBundles::operator()(
1223 const HandlerBundle
& hb
) {
1224 auto const& lhs
= *m_cufIter
;
1225 auto const& rhs
= *hb
.m_cufIter
;
1227 if (lhs
.ctx() != rhs
.ctx()) {
1228 // We only consider ObjectData* for equality (not a Class*) so if either is
1229 // an object these are not considered equal.
1230 if (!(uintptr_t(lhs
.ctx()) & 1) || !(uintptr_t(rhs
.ctx()) & 1)) {
1235 return lhs
.func() == rhs
.func();
1238 bool AutoloadHandler::addHandler(const Variant
& handler
, bool prepend
) {
1239 SmartCufIterPtr cufIter
= nullptr;
1240 if (!vm_decode_function_cufiter(handler
, cufIter
)) {
1244 m_spl_stack_inited
= true;
1246 // Zend doesn't modify the order of the list if the handler is already
1248 auto const& compareBundles
= CompareBundles(cufIter
.get());
1249 if (std::find_if(m_handlers
.begin(), m_handlers
.end(), compareBundles
) !=
1255 m_handlers
.emplace_back(handler
, cufIter
);
1257 m_handlers
.emplace_front(handler
, cufIter
);
1263 bool AutoloadHandler::isRunning() {
1264 return !m_loading
.empty();
1267 void AutoloadHandler::removeHandler(const Variant
& handler
) {
1268 SmartCufIterPtr cufIter
= nullptr;
1269 if (!vm_decode_function_cufiter(handler
, cufIter
)) {
1273 // Use find_if instead of remove_if since we know there can only be one match
1275 auto const& compareBundles
= CompareBundles(cufIter
.get());
1276 auto it
= std::find_if(m_handlers
.begin(), m_handlers
.end(), compareBundles
);
1277 if (it
!= m_handlers
.end()) {
1278 m_handlers
.erase(it
);
1282 void AutoloadHandler::removeAllHandlers() {
1283 m_spl_stack_inited
= false;
1287 bool function_exists(const String
& function_name
) {
1288 auto f
= HPHP::Unit::lookupFunc(function_name
.get());
1289 return (f
!= nullptr) &&
1290 (f
->builtinFuncPtr() != Native::unimplementedWrapper
);
1293 ///////////////////////////////////////////////////////////////////////////////
1294 // debugger and code coverage instrumentation
1296 void throw_exception(const Object
& e
) {
1297 if (!e
.instanceof(SystemLib::s_ExceptionClass
)) {
1298 raise_error("Exceptions must be valid objects derived from the "
1299 "Exception base class");
1301 DEBUGGER_ATTACHED_ONLY(phpDebuggerExceptionThrownHook(e
.get()));
1305 ///////////////////////////////////////////////////////////////////////////////