2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 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_class.h"
31 #include "hphp/runtime/ext/ext_function.h"
32 #include "hphp/runtime/ext/ext_file.h"
33 #include "hphp/runtime/ext/ext_collections.h"
34 #include "hphp/util/logger.h"
35 #include "hphp/util/util.h"
36 #include "hphp/util/process.h"
37 #include "hphp/runtime/vm/repo.h"
38 #include "hphp/runtime/vm/jit/translator.h"
39 #include "hphp/runtime/vm/jit/translator-inline.h"
40 #include "hphp/runtime/vm/unit.h"
41 #include "hphp/runtime/vm/event_hook.h"
42 #include "hphp/system/lib/systemlib.h"
46 using namespace HPHP::MethodLookup
;
49 ///////////////////////////////////////////////////////////////////////////////
52 const StaticString
s_offsetExists("offsetExists");
53 const StaticString
s___autoload("__autoload");
54 const StaticString
s___call("__call");
55 const StaticString
s___callStatic("__callStatic");
56 const StaticString
s_exception("exception");
57 const StaticString
s_previous("previous");
59 const StaticString
s_self("self");
60 const StaticString
s_parent("parent");
61 const StaticString
s_static("static");
62 const StaticString
s_class("class");
63 const StaticString
s_function("function");
64 const StaticString
s_constant("constant");
65 const StaticString
s_type("type");
66 const StaticString
s_failure("failure");
68 const StaticString
s_Traversable("Traversable");
69 const StaticString
s_KeyedTraversable("KeyedTraversable");
70 const StaticString
s_Indexish("Indexish");
72 ///////////////////////////////////////////////////////////////////////////////
74 bool array_is_valid_callback(CArrRef arr
) {
75 if (arr
.size() != 2 || !arr
.exists(int64_t(0)) || !arr
.exists(int64_t(1))) {
78 Variant elem0
= arr
.rvalAt(int64_t(0));
79 if (!elem0
.isString() && !elem0
.isObject()) {
82 Variant elem1
= arr
.rvalAt(int64_t(1));
83 if (!elem1
.isString()) {
90 vm_decode_function(CVarRef function
,
96 bool warn
/* = true */) {
98 if (function
.isString() || function
.isArray()) {
99 HPHP::Class
* ctx
= nullptr;
100 if (ar
) ctx
= arGetContextClass(ar
);
101 // Decode the 'function' parameter into this_, cls, name, pos, and
102 // nameContainsClass.
106 int pos
= String::npos
;
107 bool nameContainsClass
= false;
108 if (function
.isString()) {
109 // If 'function' is a string we simply assign it to name and
110 // leave this_ and cls set to NULL.
111 name
= function
.toString();
112 pos
= name
.find("::");
114 (pos
!= 0 && pos
!= String::npos
&& pos
+ 2 < name
.size());
116 // If 'function' is an array with exactly two indices 0 and 1, we
117 // assign the value at index 1 to name and we use the value at index
118 // 0 to populate cls (if the value is a string) or this_ and cls (if
119 // the value is an object).
120 assert(function
.isArray());
121 Array arr
= function
.toArray();
122 if (!array_is_valid_callback(arr
)) {
124 throw_invalid_argument("function: not a valid callback array");
128 Variant elem1
= arr
.rvalAt(int64_t(1));
129 name
= elem1
.toString();
130 pos
= name
.find("::");
132 (pos
!= 0 && pos
!= String::npos
&& pos
+ 2 < name
.size());
133 Variant elem0
= arr
.rvalAt(int64_t(0));
134 if (elem0
.isString()) {
135 String sclass
= elem0
.toString();
136 if (sclass
->isame(s_self
.get())) {
140 if (!nameContainsClass
) {
143 } else if (sclass
->isame(s_parent
.get())) {
144 if (ctx
&& ctx
->parent()) {
147 if (!nameContainsClass
) {
150 } else if (sclass
->isame(s_static
.get())) {
153 cls
= ar
->getThis()->getVMClass();
154 } else if (ar
->hasClass()) {
155 cls
= ar
->getClass();
159 if (warn
&& nameContainsClass
) {
160 String nameClass
= name
.substr(0, pos
);
161 if (nameClass
->isame(s_self
.get()) ||
162 nameClass
->isame(s_parent
.get()) ||
163 nameClass
->isame(s_static
.get())) {
164 raise_warning("behavior of call_user_func(array('%s', '%s')) "
165 "is undefined", sclass
->data(), name
->data());
168 cls
= Unit::loadClass(sclass
.get());
172 throw_invalid_argument("function: class not found");
177 assert(elem0
.isObject());
178 this_
= elem0
.getObjectData();
179 cls
= this_
->getVMClass();
183 HPHP::Class
* cc
= cls
;
184 if (nameContainsClass
) {
185 String c
= name
.substr(0, pos
);
186 name
= name
.substr(pos
+ 2);
187 if (c
->isame(s_self
.get())) {
196 } else if (c
->isame(s_parent
.get())) {
199 } else if (ctx
&& ctx
->parent()) {
205 } else if (c
->isame(s_static
.get())) {
208 cc
= ar
->getThis()->getVMClass();
209 } else if (ar
->hasClass()) {
214 cc
= Unit::loadClass(c
.get());
218 throw_invalid_argument("function: class not found");
223 if (!cls
->classof(cc
)) {
225 raise_warning("call_user_func expects parameter 1 to be a valid "
226 "callback, class '%s' is not a subclass of '%s'",
227 cls
->preClass()->name()->data(),
228 cc
->preClass()->name()->data());
233 // If there is not a current instance, cc trumps cls.
239 HPHP::Func
* f
= HPHP::Unit::loadFunc(name
.get());
242 throw_invalid_argument("function: method '%s' not found",
247 assert(f
&& f
->preClass() == nullptr);
251 CallType lookupType
= this_
? ObjMethod
: ClsMethod
;
252 const HPHP::Func
* f
=
253 g_vmContext
->lookupMethodCtx(cc
, name
.get(), ctx
, lookupType
);
254 if (f
&& (f
->attrs() & AttrStatic
)) {
255 // If we found a method and its static, null out this_
259 // If we did not find a static method AND this_ is null AND there is a
260 // frame ar, check if the current instance from ar is compatible
261 ObjectData
* obj
= ar
->hasThis() ? ar
->getThis() : nullptr;
262 if (obj
&& obj
->instanceof(cls
)) {
264 cls
= obj
->getVMClass();
269 // If this_ is non-null AND we could not find a method, try
270 // looking up __call in cls's method table
271 f
= cls
->lookupMethod(s___call
.get());
272 assert(!f
|| !(f
->attrs() & AttrStatic
));
274 if (!f
&& lookupType
== ClsMethod
) {
275 f
= cls
->lookupMethod(s___callStatic
.get());
276 assert(!f
|| (f
->attrs() & AttrStatic
));
280 // We found __call or __callStatic!
281 // Stash the original name into invName.
282 invName
= name
.get();
283 invName
->incRefCount();
285 // Bail out if we couldn't find the method or __call
287 throw_invalid_argument("function: method '%s' not found",
294 assert(f
&& f
->preClass());
295 // If this_ is non-NULL, then this_ is the current instance and cls is
296 // the class of the current instance.
297 assert(!this_
|| this_
->getVMClass() == cls
);
298 // If we are doing a forwarding call and this_ is null, set cls
299 // appropriately to propagate the current late bound class.
300 if (!this_
&& forwarding
&& ar
) {
301 HPHP::Class
* fwdCls
= nullptr;
302 ObjectData
* obj
= ar
->hasThis() ? ar
->getThis() : nullptr;
304 fwdCls
= obj
->getVMClass();
305 } else if (ar
->hasClass()) {
306 fwdCls
= ar
->getClass();
308 // Only forward the current late bound class if it is the same or
309 // a descendent of cls
310 if (fwdCls
&& fwdCls
->classof(cls
)) {
316 if (function
.isObject()) {
317 static StringData
* invokeStr
= StringData::GetStaticString("__invoke");
318 this_
= function
.asCObjRef().get();
320 const HPHP::Func
*f
= this_
->getVMClass()->lookupMethod(invokeStr
);
322 ((f
->attrs() & AttrStatic
) && !f
->isClosureBody())) {
323 // If __invoke is static, invoke it as such
324 cls
= this_
->getVMClass();
330 throw_invalid_argument("function: not string, closure, or array");
335 Variant
vm_call_user_func(CVarRef function
, CArrRef params
,
336 bool forwarding
/* = false */) {
337 ObjectData
* obj
= nullptr;
338 HPHP::Class
* cls
= nullptr;
339 HPHP::Transl::CallerFrame cf
;
340 StringData
* invName
= nullptr;
341 const HPHP::Func
* f
= vm_decode_function(function
, cf(), forwarding
,
344 return uninit_null();
347 g_vmContext
->invokeFunc((TypedValue
*)&ret
, f
, params
, obj
, cls
,
352 Variant
invoke(CStrRef function
, CArrRef params
, strhash_t hash
/* = -1 */,
353 bool tryInterp
/* = true */, bool fatal
/* = true */) {
354 Func
* func
= Unit::loadFunc(function
.get());
357 g_vmContext
->invokeFunc(ret
.asTypedValue(), func
, params
);
360 return invoke_failed(function
.c_str(), params
, fatal
);
363 Variant
invoke(const char *function
, CArrRef params
, strhash_t hash
/* = -1*/,
364 bool tryInterp
/* = true */, bool fatal
/* = true */) {
365 String
funcName(function
, CopyString
);
366 return invoke(funcName
, params
, hash
, tryInterp
, fatal
);
369 Variant
invoke_static_method(CStrRef s
, CStrRef method
, CArrRef params
,
370 bool fatal
/* = true */) {
371 HPHP::Class
* class_
= Unit::lookupClass(s
.get());
372 if (class_
== nullptr) {
373 o_invoke_failed(s
.data(), method
.data(), fatal
);
374 return uninit_null();
376 const HPHP::Func
* f
= class_
->lookupMethod(method
.get());
377 if (f
== nullptr || !(f
->attrs() & AttrStatic
)) {
378 o_invoke_failed(s
.data(), method
.data(), fatal
);
379 return uninit_null();
382 g_vmContext
->invokeFunc((TypedValue
*)&ret
, f
, params
, nullptr, class_
);
386 Variant
invoke_failed(CVarRef func
, CArrRef params
,
387 bool fatal
/* = true */) {
388 if (func
.isObject()) {
389 return o_invoke_failed(
390 func
.objectForCall()->o_getClassName().c_str(),
393 return invoke_failed(func
.toString().c_str(), params
, fatal
);
397 Variant
invoke_failed(const char *func
, CArrRef params
,
398 bool fatal
/* = true */) {
400 throw InvalidFunctionCallException(func
);
402 raise_warning("call_user_func to non-existent function %s", func
);
407 Variant
o_invoke_failed(const char *cls
, const char *meth
,
408 bool fatal
/* = true */) {
410 string msg
= "Unknown method ";
414 throw FatalErrorException(msg
.c_str());
416 raise_warning("call_user_func to non-existent method %s::%s", cls
, meth
);
421 void NEVER_INLINE
raise_null_object_prop() {
422 raise_notice("Trying to get property of non-object");
425 void NEVER_INLINE
throw_null_get_object_prop() {
426 raise_error("Trying to get property of non-object");
429 void NEVER_INLINE
throw_null_object_prop() {
430 raise_error("Trying to set property of non-object");
433 void NEVER_INLINE
throw_invalid_property_name(CStrRef name
) {
435 throw EmptyObjectPropertyException();
437 throw NullStartObjectPropertyException();
441 void throw_instance_method_fatal(const char *name
) {
442 if (!strstr(name
, "::__destruct")) {
443 raise_error("Non-static method %s() cannot be called statically", name
);
447 void throw_iterator_not_valid() {
448 Object
e(SystemLib::AllocInvalidOperationExceptionObject(
449 "Iterator is not valid"));
453 void throw_collection_modified() {
454 Object
e(SystemLib::AllocInvalidOperationExceptionObject(
455 "Collection was modified during iteration"));
459 void throw_collection_property_exception() {
460 Object
e(SystemLib::AllocInvalidOperationExceptionObject(
461 "Cannot access a property on a collection"));
465 void throw_collection_compare_exception() {
466 static const string
msg(
467 "Cannot use relational comparison operators (<, <=, >, >=) to compare "
468 "a collection with an integer, double, string, array, or object");
469 if (RuntimeOption::StrictCollections
) {
470 Object
e(SystemLib::AllocRuntimeExceptionObject(msg
));
476 void check_collection_compare(ObjectData
* obj
) {
477 if (obj
&& obj
->isCollection()) throw_collection_compare_exception();
480 void check_collection_compare(ObjectData
* obj1
, ObjectData
* obj2
) {
481 if (obj1
&& obj2
&& (obj1
->isCollection() || obj2
->isCollection())) {
482 throw_collection_compare_exception();
486 void check_collection_cast_to_array() {
487 if (RuntimeOption::WarnOnCollectionToArray
) {
488 raise_warning("Casting a collection to an array is an expensive operation "
489 "and should be avoided where possible. To convert a "
490 "collection to an array without raising a warning, use the "
491 "toArray() method.");
495 Object
create_object_only(CStrRef s
) {
496 return g_vmContext
->createObjectOnly(s
.get());
499 Object
create_object(CStrRef s
, CArrRef params
, bool init
/* = true */) {
500 return g_vmContext
->createObject(s
.get(), params
, init
);
504 * This function is used when another thread is segfaulting---we just
505 * want to wait forever to give it a chance to write a stacktrace file
506 * (and maybe a core file).
508 void pause_forever() {
512 ssize_t
check_request_surprise(ThreadInfo
*info
) {
513 RequestInjectionData
&p
= info
->m_reqInjectionData
;
514 bool do_timedout
, do_memExceeded
, do_signaled
;
516 ssize_t flags
= p
.fetchAndClearFlags();
517 do_timedout
= (flags
& RequestInjectionData::TimedOutFlag
) &&
519 do_memExceeded
= (flags
& RequestInjectionData::MemExceededFlag
);
520 do_signaled
= (flags
& RequestInjectionData::SignaledFlag
);
522 // Start with any pending exception that might be on the thread.
523 Exception
* pendingException
= info
->m_pendingException
;
524 info
->m_pendingException
= nullptr;
526 if (do_timedout
&& !pendingException
) {
527 pendingException
= generate_request_timeout_exception();
529 if (do_memExceeded
&& !pendingException
) {
530 pendingException
= generate_memory_exceeded_exception();
532 if (do_signaled
) f_pcntl_signal_dispatch();
534 if (pendingException
) {
535 pendingException
->throwException();
540 void throw_missing_arguments_nr(const char *fn
, int expected
, int got
,
541 int level
/* = 0 */) {
542 if (level
== 2 || RuntimeOption::ThrowMissingArguments
) {
544 raise_error(Strings::MISSING_ARGUMENT
, fn
, got
);
546 raise_error(Strings::MISSING_ARGUMENTS
, fn
, expected
, got
);
550 raise_warning(Strings::MISSING_ARGUMENT
, fn
, got
);
552 raise_warning(Strings::MISSING_ARGUMENTS
, fn
, expected
, got
);
557 void throw_toomany_arguments_nr(const char *fn
, int num
, int level
/* = 0 */) {
558 if (level
== 2 || RuntimeOption::ThrowTooManyArguments
) {
559 raise_error("Too many arguments for %s(), expected %d", fn
, num
);
560 } else if (level
== 1 || RuntimeOption::WarnTooManyArguments
) {
561 raise_warning("Too many arguments for %s(), expected %d", fn
, num
);
565 void throw_wrong_arguments_nr(const char *fn
, int count
, int cmin
, int cmax
,
566 int level
/* = 0 */) {
567 if (cmin
>= 0 && count
< cmin
) {
568 throw_missing_arguments_nr(fn
, cmin
, count
, level
);
571 if (cmax
>= 0 && count
> cmax
) {
572 throw_toomany_arguments_nr(fn
, cmax
, level
);
578 void throw_bad_type_exception(const char *fmt
, ...) {
582 Util::string_vsnprintf(msg
, fmt
, ap
);
585 if (RuntimeOption::ThrowBadTypeExceptions
) {
586 throw InvalidOperandException(msg
.c_str());
589 raise_warning("Invalid operand type was used: %s", msg
.c_str());
592 void throw_bad_array_exception() {
593 const char* fn
= "(unknown)";
594 ActRec
*ar
= g_vmContext
->getStackFrame();
596 fn
= ar
->m_func
->name()->data();
598 throw_bad_type_exception("%s expects array(s)", fn
);
601 void throw_invalid_argument(const char *fmt
, ...) {
605 Util::string_vsnprintf(msg
, fmt
, ap
);
608 if (RuntimeOption::ThrowInvalidArguments
) {
609 throw InvalidArgumentException(msg
.c_str());
612 raise_warning("Invalid argument: %s", msg
.c_str());
615 Variant
throw_fatal_unset_static_property(const char *s
, const char *prop
) {
616 raise_error("Attempt to unset static property %s::$%s", s
, prop
);
617 return uninit_null();
620 void throw_infinite_recursion_exception() {
621 if (!RuntimeOption::NoInfiniteRecursionDetection
) {
622 // Reset profiler otherwise it might recurse further causing segfault
624 info
->m_profiler
= nullptr;
625 throw UncatchableException("infinite recursion detected");
628 Exception
* generate_request_timeout_exception() {
629 Exception
* ret
= nullptr;
630 ThreadInfo
*info
= ThreadInfo::s_threadInfo
.getNoCheck();
631 RequestInjectionData
&data
= info
->m_reqInjectionData
;
632 if (data
.timeoutSeconds
> 0) {
633 // This extra checking is needed, because there may be a race condition
634 // a TimeoutThread sets flag "true" right after an old request finishes and
635 // right before a new requets resets "started". In this case, we flag
636 // "timedout" back to "false".
637 if (time(0) - data
.started
>= data
.timeoutSeconds
) {
638 std::string exceptionMsg
= "entire web request took longer than ";
639 exceptionMsg
+= boost::lexical_cast
<std::string
>(data
.timeoutSeconds
);
640 exceptionMsg
+= " seconds and timed out";
641 ArrayHolder exceptionStack
;
642 if (RuntimeOption::InjectedStackTrace
) {
643 exceptionStack
= g_vmContext
->debugBacktrace(false, true, true).get();
645 ret
= new FatalErrorException(exceptionMsg
, exceptionStack
.get());
651 Exception
* generate_memory_exceeded_exception() {
652 ArrayHolder exceptionStack
;
653 if (RuntimeOption::InjectedStackTrace
) {
654 exceptionStack
= g_vmContext
->debugBacktrace(false, true, true).get();
656 return new FatalErrorException(
657 "request has exceeded memory limit", exceptionStack
.get());
660 void throw_call_non_object() {
661 throw_call_non_object(nullptr);
664 void throw_call_non_object(const char *methodName
) {
667 if (methodName
== nullptr) {
668 msg
= "Call to a member function on a non-object";
670 Util::string_printf(msg
,
671 "Call to a member function %s() on a non-object",
675 if (RuntimeOption::ThrowExceptionOnBadMethodCall
) {
676 Object
e(SystemLib::AllocBadMethodCallExceptionObject(String(msg
)));
680 throw FatalErrorException(msg
.c_str());
683 void throw_unexpected_argument_type(int argNum
, const char *fnName
,
684 const char *expected
, CVarRef val
) {
685 const char *otype
= nullptr;
686 switch (val
.getType()) {
688 case KindOfNull
: otype
= "null"; break;
689 case KindOfBoolean
: otype
= "bool"; break;
690 case KindOfInt64
: otype
= "int"; break;
691 case KindOfDouble
: otype
= "double"; break;
692 case KindOfStaticString
:
693 case KindOfString
: otype
= "string"; break;
694 case KindOfArray
: otype
= "array"; break;
696 otype
= val
.getObjectData()->o_getClassName().c_str();
701 raise_recoverable_error
702 ("Argument %d passed to %s must be an instance of %s, %s given",
703 argNum
, fnName
, expected
, otype
);
706 Object
f_clone(CVarRef v
) {
708 Object clone
= Object(v
.toObject()->clone());
712 raise_error("Cannot clone non-object");
716 String
f_serialize(CVarRef value
) {
717 switch (value
.getType()) {
722 return value
.getBoolean() ? "b:1;" : "b:0;";
726 sb
.append(value
.getInt64());
730 case KindOfStaticString
:
732 StringData
*str
= value
.getStringData();
735 sb
.append(str
->size());
737 sb
.append(str
->data(), str
->size());
742 ArrayData
*arr
= value
.getArrayData();
743 if (arr
->empty()) return "a:0:{}";
748 VariableSerializer
vs(VariableSerializer::Serialize
);
749 return vs
.serialize(value
, true);
758 Variant
unserialize_ex(const char* str
, int len
,
759 VariableUnserializer::Type type
,
760 CArrRef class_whitelist
/* = null_array */) {
761 if (str
== nullptr || len
<= 0) {
765 VariableUnserializer
vu(str
, len
, type
, false, class_whitelist
);
768 v
= vu
.unserialize();
769 } catch (FatalErrorException
&e
) {
771 } catch (Exception
&e
) {
772 raise_notice("Unable to unserialize: [%s]. %s.", str
,
773 e
.getMessage().c_str());
779 Variant
unserialize_ex(CStrRef str
,
780 VariableUnserializer::Type type
,
781 CArrRef class_whitelist
/* = null_array */) {
782 return unserialize_ex(str
.data(), str
.size(), type
, class_whitelist
);
785 String
concat3(CStrRef s1
, CStrRef s2
, CStrRef s3
) {
786 StringSlice r1
= s1
.slice();
787 StringSlice r2
= s2
.slice();
788 StringSlice r3
= s3
.slice();
789 int len
= r1
.len
+ r2
.len
+ r3
.len
;
790 StringData
* str
= NEW(StringData
)(len
);
791 MutableSlice r
= str
->mutableSlice();
792 memcpy(r
.ptr
, r1
.ptr
, r1
.len
);
793 memcpy(r
.ptr
+ r1
.len
, r2
.ptr
, r2
.len
);
794 memcpy(r
.ptr
+ r1
.len
+ r2
.len
, r3
.ptr
, r3
.len
);
799 String
concat4(CStrRef s1
, CStrRef s2
, CStrRef s3
, CStrRef s4
) {
800 StringSlice r1
= s1
.slice();
801 StringSlice r2
= s2
.slice();
802 StringSlice r3
= s3
.slice();
803 StringSlice r4
= s4
.slice();
804 int len
= r1
.len
+ r2
.len
+ r3
.len
+ r4
.len
;
805 StringData
* str
= NEW(StringData
)(len
);
806 MutableSlice r
= str
->mutableSlice();
807 memcpy(r
.ptr
, r1
.ptr
, r1
.len
);
808 memcpy(r
.ptr
+ r1
.len
, r2
.ptr
, r2
.len
);
809 memcpy(r
.ptr
+ r1
.len
+ r2
.len
, r3
.ptr
, r3
.len
);
810 memcpy(r
.ptr
+ r1
.len
+ r2
.len
+ r3
.len
, r4
.ptr
, r4
.len
);
815 String
concat5(CStrRef s1
, CStrRef s2
, CStrRef s3
, CStrRef s4
, CStrRef s5
) {
816 int len1
= s1
.size();
817 int len2
= s2
.size();
818 int len3
= s3
.size();
819 int len4
= s4
.size();
820 int len5
= s5
.size();
821 int len
= len1
+ len2
+ len3
+ len4
+ len5
;
822 String s
= String(len
, ReserveString
);
823 char *buf
= s
.mutableSlice().ptr
;
824 memcpy(buf
, s1
.data(), len1
);
825 memcpy(buf
+ len1
, s2
.data(), len2
);
826 memcpy(buf
+ len1
+ len2
, s3
.data(), len3
);
827 memcpy(buf
+ len1
+ len2
+ len3
, s4
.data(), len4
);
828 memcpy(buf
+ len1
+ len2
+ len3
+ len4
, s5
.data(), len5
);
829 return s
.setSize(len
);
832 String
concat6(CStrRef s1
, CStrRef s2
, CStrRef s3
, CStrRef s4
, CStrRef s5
,
834 int len1
= s1
.size();
835 int len2
= s2
.size();
836 int len3
= s3
.size();
837 int len4
= s4
.size();
838 int len5
= s5
.size();
839 int len6
= s6
.size();
840 int len
= len1
+ len2
+ len3
+ len4
+ len5
+ len6
;
841 String s
= String(len
, ReserveString
);
842 char *buf
= s
.mutableSlice().ptr
;
843 memcpy(buf
, s1
.data(), len1
);
844 memcpy(buf
+ len1
, s2
.data(), len2
);
845 memcpy(buf
+ len1
+ len2
, s3
.data(), len3
);
846 memcpy(buf
+ len1
+ len2
+ len3
, s4
.data(), len4
);
847 memcpy(buf
+ len1
+ len2
+ len3
+ len4
, s5
.data(), len5
);
848 memcpy(buf
+ len1
+ len2
+ len3
+ len4
+ len5
, s6
.data(), len6
);
849 return s
.setSize(len
);
852 bool empty(CVarRef v
, bool offset
) {
853 return empty(v
, Variant(offset
));
855 bool empty(CVarRef v
, int64_t offset
) {
856 Variant::TypedValueAccessor tva
= v
.getTypedAccessor();
857 if (LIKELY(Variant::GetAccessorType(tva
) == KindOfArray
)) {
858 return empty(Variant::GetArrayData(tva
)->get(offset
));
860 return empty(v
, VarNR(offset
));
862 bool empty(CVarRef v
, double offset
) {
863 return empty(v
, VarNR(offset
));
865 bool empty(CVarRef v
, CArrRef offset
) {
866 return empty(v
, VarNR(offset
));
868 bool empty(CVarRef v
, CObjRef offset
) {
869 return empty(v
, VarNR(offset
));
872 bool empty(CVarRef v
, CStrRef offset
, bool isString
/* = false */) {
873 Variant::TypedValueAccessor tva
= v
.getTypedAccessor();
874 if (LIKELY(Variant::GetAccessorType(tva
) == KindOfArray
)) {
875 return empty(Variant::GetAsArray(tva
).
876 rvalAtRef(offset
, AccessFlags::IsKey(isString
)));
878 return empty(v
, VarNR(offset
));
881 bool empty(CVarRef v
, CVarRef offset
) {
882 Variant::TypedValueAccessor tva
= v
.getTypedAccessor();
883 if (LIKELY(Variant::GetAccessorType(tva
) == KindOfArray
)) {
884 return empty(Variant::GetAsArray(tva
).rvalAtRef(offset
));
886 if (Variant::GetAccessorType(tva
) == KindOfObject
) {
887 ObjectData
* obj
= Variant::GetObjectData(tva
);
888 if (obj
->isCollection()) {
889 return collectionOffsetEmpty(obj
, offset
);
891 if (!Variant::GetArrayAccess(tva
)->
892 o_invoke_few_args(s_offsetExists
, 1, offset
)) {
895 return empty(v
.rvalAt(offset
));
897 } else if (Variant::IsString(tva
)) {
898 uint64_t pos
= offset
.toInt64();
899 if (pos
>= (uint64_t)Variant::GetStringData(tva
)->size()) {
903 return empty(v
.rvalAt(offset
));
906 bool isset(CArrRef v
, int64_t offset
) {
907 return isset(v
.rvalAtRef(offset
));
909 bool isset(CArrRef v
, CArrRef offset
) {
910 return isset(v
, VarNR(offset
));
912 bool isset(CArrRef v
, CObjRef offset
) {
913 return isset(v
, VarNR(offset
));
915 bool isset(CArrRef v
, CStrRef offset
, bool isString
/* = false */) {
916 return isset(v
.rvalAtRef(offset
, AccessFlags::IsKey(isString
)));
918 bool isset(CArrRef v
, CVarRef offset
) {
919 return isset(v
.rvalAtRef(offset
));
922 bool isset(CVarRef v
, bool offset
) {
923 return isset(v
, VarNR(offset
));
925 bool isset(CVarRef v
, int64_t offset
) {
926 Variant::TypedValueAccessor tva
= v
.getTypedAccessor();
927 if (LIKELY(Variant::GetAccessorType(tva
) == KindOfArray
)) {
928 return isset(Variant::GetArrayData(tva
)->get(offset
));
930 if (Variant::GetAccessorType(tva
) == KindOfObject
) {
931 ObjectData
* obj
= Variant::GetObjectData(tva
);
932 if (obj
->isCollection()) {
933 return collectionOffsetIsset(obj
, offset
);
935 return Variant::GetArrayAccess(tva
)->
936 o_invoke_few_args(s_offsetExists
, 1, offset
);
939 if (Variant::IsString(tva
)) {
940 return (uint64_t)offset
< (uint64_t)Variant::GetStringData(tva
)->size();
944 bool isset(CVarRef v
, double offset
) {
945 return isset(v
, VarNR(offset
));
947 bool isset(CVarRef v
, CArrRef offset
) {
948 return isset(v
, VarNR(offset
));
950 bool isset(CVarRef v
, CObjRef offset
) {
951 return isset(v
, VarNR(offset
));
953 bool isset(CVarRef v
, CVarRef offset
) {
954 Variant::TypedValueAccessor tva
= v
.getTypedAccessor();
955 if (LIKELY(Variant::GetAccessorType(tva
) == KindOfArray
)) {
956 return isset(Variant::GetAsArray(tva
).rvalAtRef(offset
));
958 if (Variant::GetAccessorType(tva
) == KindOfObject
) {
959 ObjectData
* obj
= Variant::GetObjectData(tva
);
960 if (obj
->isCollection()) {
961 return collectionOffsetIsset(obj
, offset
);
963 return Variant::GetArrayAccess(tva
)->
964 o_invoke_few_args(s_offsetExists
, 1, offset
);
967 if (Variant::IsString(tva
)) {
968 uint64_t pos
= offset
.toInt64();
969 return pos
< (uint64_t)Variant::GetStringData(tva
)->size();
974 bool isset(CVarRef v
, CStrRef offset
, bool isString
/* = false */) {
975 Variant::TypedValueAccessor tva
= v
.getTypedAccessor();
976 if (LIKELY(Variant::GetAccessorType(tva
) == KindOfArray
)) {
977 return isset(Variant::GetAsArray(tva
).rvalAtRef(
978 offset
, AccessFlags::IsKey(isString
)));
980 if (Variant::GetAccessorType(tva
) == KindOfObject
||
981 Variant::IsString(tva
)) {
982 return isset(v
, Variant(offset
));
987 bool interface_supports_array(const StringData
* s
) {
988 return (s
->isame(s_Traversable
.get()) ||
989 s
->isame(s_KeyedTraversable
.get()) ||
990 s
->isame(s_Indexish
.get()));
993 bool interface_supports_array(const std::string
& n
) {
994 const char* s
= n
.c_str();
995 return ((n
.size() == 11 && !strcasecmp(s
, "Traversable")) ||
996 (n
.size() == 16 && !strcasecmp(s
, "KeyedTraversable")) ||
997 (n
.size() == 8 && !strcasecmp(s
, "Indexish")));
1000 String
get_source_filename(litstr path
, bool dir_component
/* = false */) {
1002 if (path
[0] == '/') {
1005 ret
= RuntimeOption::SourceRoot
+ path
;
1008 if (dir_component
) {
1009 return f_dirname(ret
);
1015 Variant
include_impl_invoke(CStrRef file
, bool once
, const char *currentDir
) {
1016 if (file
[0] == '/') {
1017 if (RuntimeOption::SandboxMode
|| !RuntimeOption::AlwaysUseRelativePath
) {
1019 return invoke_file(file
, once
, currentDir
);
1020 } catch(PhpFileDoesNotExistException
&e
) {}
1023 String
rel_path(Util::relativePath(RuntimeOption::SourceRoot
,
1024 string(file
.data())));
1026 // Don't try/catch - We want the exception to be passed along
1027 return invoke_file(rel_path
, once
, currentDir
);
1029 // Don't try/catch - We want the exception to be passed along
1030 return invoke_file(file
, once
, currentDir
);
1034 Variant
invoke_file(CStrRef s
, bool once
, const char *currentDir
) {
1036 if (invoke_file_impl(r
, s
, once
, currentDir
)) {
1039 return throw_missing_file(s
.c_str());
1042 bool invoke_file_impl(Variant
&res
, CStrRef path
, bool once
,
1043 const char *currentDir
) {
1045 HPHP::Eval::PhpFile
* efile
=
1046 g_vmContext
->lookupPhpFile(path
.get(), currentDir
, &initial
);
1047 HPHP::Unit
* u
= nullptr;
1048 if (efile
) u
= efile
->unit();
1052 if (!once
|| initial
) {
1053 g_vmContext
->invokeUnit((TypedValue
*)(&res
), u
);
1059 * Used by include_impl. resolve_include() needs some way of checking the
1060 * existence of a file path, which for hphpc means attempting to invoke it.
1061 * This struct carries some context information needed for the invocation, as
1062 * well as a place for the return value of invoking the file.
1064 struct IncludeImplInvokeContext
{
1066 const char* currentDir
;
1068 Variant returnValue
;
1071 static bool include_impl_invoke_context(CStrRef file
, void* ctx
) {
1072 struct IncludeImplInvokeContext
* context
= (IncludeImplInvokeContext
*)ctx
;
1073 bool invoked_file
= false;
1075 context
->returnValue
= include_impl_invoke(file
, context
->once
,
1076 context
->currentDir
);
1077 invoked_file
= true;
1078 } catch (PhpFileDoesNotExistException
& e
) {
1079 context
->returnValue
= false;
1081 return invoked_file
;
1085 * tryFile is a pointer to a function that resolve_include() will use to
1086 * determine if a path references a real file. ctx is a pointer to some context
1087 * information that will be passed through to tryFile. (It's a hacky closure)
1089 String
resolve_include(CStrRef file
, const char* currentDir
,
1090 bool (*tryFile
)(CStrRef file
, void*), void* ctx
) {
1091 const char* c_file
= file
->data();
1093 if (c_file
[0] == '/') {
1094 String
can_path(Util::canonicalize(file
.c_str(), file
.size()),
1097 if (tryFile(can_path
, ctx
)) {
1101 } else if ((c_file
[0] == '.' && (c_file
[1] == '/' || (
1102 c_file
[1] == '.' && c_file
[2] == '/')))) {
1104 String
path(String(g_context
->getCwd() + "/" + file
));
1105 String
can_path(Util::canonicalize(path
.c_str(), path
.size()),
1108 if (tryFile(can_path
, ctx
)) {
1113 Array includePaths
= g_context
->getIncludePathArray();
1114 unsigned int path_count
= includePaths
.size();
1116 for (int i
= 0; i
< (int)path_count
; i
++) {
1118 String includePath
= includePaths
[i
];
1120 if (includePath
[0] != '/') {
1121 path
+= (g_context
->getCwd() + "/");
1124 path
+= includePath
;
1126 if (path
[path
.size() - 1] != '/') {
1131 String
can_path(Util::canonicalize(path
.c_str(), path
.size()),
1134 if (tryFile(can_path
, ctx
)) {
1139 if (currentDir
[0] == '/') {
1140 String
path(currentDir
);
1143 String
can_path(Util::canonicalize(path
.c_str(), path
.size()),
1146 if (tryFile(can_path
, ctx
)) {
1150 String
path(g_context
->getCwd() + "/" + currentDir
+ file
);
1151 String
can_path(Util::canonicalize(path
.c_str(), path
.size()),
1154 if (tryFile(can_path
, ctx
)) {
1163 static Variant
include_impl(CStrRef file
, bool once
,
1164 const char *currentDir
, bool required
,
1166 struct IncludeImplInvokeContext ctx
= {once
, currentDir
};
1167 String can_path
= resolve_include(file
, currentDir
,
1168 include_impl_invoke_context
, (void*)&ctx
);
1170 if (can_path
.isNull()) {
1173 raise_notice("Tried to invoke %s but file not found.", file
->data());
1176 String ms
= "Required file that does not exist: ";
1178 throw FatalErrorException(ms
.data());
1183 return ctx
.returnValue
;
1186 Variant
include(CStrRef file
, bool once
/* = false */,
1187 const char *currentDir
/* = NULL */,
1188 bool raiseNotice
/*= true*/) {
1189 return include_impl(file
, once
, currentDir
, false, raiseNotice
);
1192 Variant
require(CStrRef file
, bool once
/* = false */,
1193 const char *currentDir
/* = NULL */,
1194 bool raiseNotice
/*= true*/) {
1195 return include_impl(file
, once
, currentDir
, true, raiseNotice
);
1198 ///////////////////////////////////////////////////////////////////////////////
1201 IMPLEMENT_REQUEST_LOCAL(AutoloadHandler
, AutoloadHandler::s_instance
);
1203 void AutoloadHandler::requestInit() {
1210 void AutoloadHandler::requestShutdown() {
1216 bool AutoloadHandler::setMap(CArrRef map
, CStrRef root
) {
1218 this->m_map_root
= root
;
1222 class ClassExistsChecker
{
1224 ClassExistsChecker() {}
1225 bool operator()(CStrRef name
) const {
1226 return Unit::lookupClass(name
.get()) != nullptr;
1230 class ConstantExistsChecker
{
1232 bool operator()(CStrRef name
) const {
1233 return Unit::lookupCns(name
.get()) != nullptr;
1238 AutoloadHandler::Result
AutoloadHandler::loadFromMap(CStrRef name
,
1241 const T
&checkExists
) {
1242 assert(!m_map
.isNull());
1244 CVarRef
&type_map
= m_map
.get()->get(kind
);
1245 Variant::TypedValueAccessor tva
= type_map
.getTypedAccessor();
1246 if (Variant::GetAccessorType(tva
) != KindOfArray
) return Failure
;
1247 String canonicalName
= toLower
? StringUtil::ToLower(name
) : name
;
1248 CVarRef
&file
= Variant::GetArrayData(tva
)->get(canonicalName
);
1250 if (file
.isString()) {
1251 String fName
= file
.toCStrRef().get();
1252 if (fName
.get()->data()[0] != '/') {
1253 if (!m_map_root
.empty()) {
1254 fName
= m_map_root
+ fName
;
1258 Transl::VMRegAnchor _
;
1260 VMExecutionContext
* ec
= g_vmContext
;
1261 Unit
* u
= ec
->evalInclude(fName
.get(), nullptr, &initial
);
1265 ec
->invokeFunc(&retval
, u
->getMain(), null_array
,
1266 nullptr, nullptr, nullptr, nullptr,
1267 ExecutionContext::InvokePseudoMain
);
1268 tvRefcountedDecRef(&retval
);
1274 if (ok
&& checkExists(name
)) {
1277 CVarRef
&func
= m_map
.get()->get(s_failure
);
1278 if (!isset(func
)) return Failure
;
1279 // can throw, otherwise
1280 // - true means the map was updated. try again
1281 // - false means we should stop applying autoloaders (only affects classes)
1282 // - anything else means keep going
1283 Variant action
= vm_call_user_func(func
, CREATE_VECTOR2(kind
, name
));
1284 tva
= action
.getTypedAccessor();
1285 if (Variant::GetAccessorType(tva
) == KindOfBoolean
) {
1286 if (Variant::GetBoolean(tva
)) continue;
1287 return StopAutoloading
;
1289 return ContinueAutoloading
;
1293 bool AutoloadHandler::autoloadFunc(StringData
* name
) {
1294 return !m_map
.isNull() &&
1295 loadFromMap(name
, s_function
, true, function_exists
) != Failure
;
1298 bool AutoloadHandler::autoloadConstant(StringData
* name
) {
1299 return !m_map
.isNull() &&
1300 loadFromMap(name
, s_constant
, false, ConstantExistsChecker()) != Failure
;
1303 bool AutoloadHandler::autoloadType(CStrRef name
) {
1304 return !m_map
.isNull() &&
1305 loadFromMap(name
, s_type
, true,
1307 return !!Unit::GetNamedEntity(name
.get())->getCachedNameDef();
1313 * invokeHandler returns true if any autoload handlers were executed,
1314 * false otherwise. When this function returns true, it is the caller's
1315 * responsibility to check if the given class or interface exists.
1317 bool AutoloadHandler::invokeHandler(CStrRef className
,
1318 bool forceSplStack
/* = false */) {
1319 if (!m_map
.isNull()) {
1320 ClassExistsChecker ce
;
1321 Result res
= loadFromMap(className
, s_class
, true, ce
);
1322 if (res
== ContinueAutoloading
) {
1323 if (ce(className
)) return true;
1325 if (res
!= Failure
) return res
== Success
;
1328 Array
params(ArrayInit(1, ArrayInit::vectorInit
).set(className
).create());
1329 bool l_running
= m_running
;
1331 if (m_handlers
.isNull() && !forceSplStack
) {
1332 if (function_exists(s___autoload
)) {
1333 invoke(s___autoload
, params
, -1, true, false);
1334 m_running
= l_running
;
1337 m_running
= l_running
;
1340 if (empty(m_handlers
)) {
1341 m_running
= l_running
;
1344 Object autoloadException
;
1345 for (ArrayIter
iter(m_handlers
); iter
; ++iter
) {
1347 vm_call_user_func(iter
.second(), params
);
1348 } catch (Object
& ex
) {
1349 assert(ex
.instanceof(SystemLib::s_ExceptionClass
));
1350 if (autoloadException
.isNull()) {
1351 autoloadException
= ex
;
1354 Variant next
= cur
->o_get(s_previous
, false, s_exception
);
1355 while (next
.isObject()) {
1356 cur
= next
.toObject();
1357 next
= cur
->o_get(s_previous
, false, s_exception
);
1359 cur
->o_set(s_previous
, autoloadException
, s_exception
);
1360 autoloadException
= ex
;
1363 if (Unit::lookupClass(className
.get()) != nullptr) {
1367 m_running
= l_running
;
1368 if (!autoloadException
.isNull()) {
1369 throw autoloadException
;
1374 bool AutoloadHandler::addHandler(CVarRef handler
, bool prepend
) {
1375 String name
= getSignature(handler
);
1376 if (name
.isNull()) return false;
1378 if (m_handlers
.isNull()) {
1379 m_handlers
= Array::Create();
1383 // The following ensures that the handler is added at the end
1384 m_handlers
.remove(name
, true);
1385 m_handlers
.add(name
, handler
, true);
1387 // This adds the handler at the beginning
1388 m_handlers
= CREATE_MAP1(name
, handler
) + m_handlers
;
1393 bool AutoloadHandler::isRunning() {
1397 void AutoloadHandler::removeHandler(CVarRef handler
) {
1398 String name
= getSignature(handler
);
1399 if (name
.isNull()) return;
1400 m_handlers
.remove(name
, true);
1403 void AutoloadHandler::removeAllHandlers() {
1407 String
AutoloadHandler::getSignature(CVarRef handler
) {
1409 if (!f_is_callable(handler
, false, ref(name
))) {
1412 String lName
= StringUtil::ToLower(name
);
1413 if (handler
.isArray()) {
1414 Variant first
= handler
.getArrayData()->get(int64_t(0));
1415 if (first
.isObject()) {
1416 // Add the object address as part of the signature
1417 int64_t data
= (int64_t)first
.getObjectData();
1418 lName
+= String((const char *)&data
, sizeof(data
), CopyString
);
1420 } else if (handler
.isObject()) {
1421 // "lName" will just be "classname::__invoke",
1422 // add object address to differentiate the signature
1423 int64_t data
= (int64_t)handler
.getObjectData();
1424 lName
+= String((const char*)&data
, sizeof(data
), CopyString
);
1429 bool function_exists(CStrRef function_name
) {
1430 return HPHP::Unit::lookupFunc(function_name
.get()) != nullptr;
1433 ///////////////////////////////////////////////////////////////////////////////
1434 // debugger and code coverage instrumentation
1436 inline void throw_exception_unchecked(CObjRef e
) {
1437 if (!Eval::Debugger::InterruptException(e
)) return;
1440 void throw_exception(CObjRef e
) {
1441 if (!e
.instanceof(SystemLib::s_ExceptionClass
)) {
1442 raise_error("Exceptions must be valid objects derived from the "
1443 "Exception base class");
1445 throw_exception_unchecked(e
);
1448 ///////////////////////////////////////////////////////////////////////////////